xibecode 0.0.1 → 0.0.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.
- package/README.md +278 -5
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +25 -2
- package/dist/commands/run.js.map +1 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +22 -0
- package/dist/core/agent.js.map +1 -1
- package/dist/core/plugins.d.ts +96 -0
- package/dist/core/plugins.d.ts.map +1 -0
- package/dist/core/plugins.js +202 -0
- package/dist/core/plugins.js.map +1 -0
- package/dist/core/tools.d.ts +21 -1
- package/dist/core/tools.d.ts.map +1 -1
- package/dist/core/tools.js +477 -3
- package/dist/core/tools.js.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/ui/enhanced-tui.d.ts +24 -0
- package/dist/ui/enhanced-tui.d.ts.map +1 -1
- package/dist/ui/enhanced-tui.js +95 -0
- package/dist/ui/enhanced-tui.js.map +1 -1
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +4 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/git.d.ts +75 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +343 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/safety.d.ts +39 -0
- package/dist/utils/safety.d.ts.map +1 -0
- package/dist/utils/safety.js +215 -0
- package/dist/utils/safety.js.map +1 -0
- package/dist/utils/testRunner.d.ts +43 -0
- package/dist/utils/testRunner.d.ts.map +1 -0
- package/dist/utils/testRunner.js +277 -0
- package/dist/utils/testRunner.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
const execAsync = promisify(exec);
|
|
4
|
+
export class GitUtils {
|
|
5
|
+
workingDir;
|
|
6
|
+
constructor(workingDir = process.cwd()) {
|
|
7
|
+
this.workingDir = workingDir;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Check if the current directory is inside a git repository
|
|
11
|
+
*/
|
|
12
|
+
async isGitRepository() {
|
|
13
|
+
try {
|
|
14
|
+
const { stdout } = await execAsync('git rev-parse --git-dir', {
|
|
15
|
+
cwd: this.workingDir,
|
|
16
|
+
timeout: 5000,
|
|
17
|
+
});
|
|
18
|
+
return stdout.trim().length > 0;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get current git status with detailed information
|
|
26
|
+
*/
|
|
27
|
+
async getStatus() {
|
|
28
|
+
const isRepo = await this.isGitRepository();
|
|
29
|
+
if (!isRepo) {
|
|
30
|
+
return { isGitRepo: false };
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
// Get branch name
|
|
34
|
+
const branchResult = await execAsync('git branch --show-current', {
|
|
35
|
+
cwd: this.workingDir,
|
|
36
|
+
timeout: 5000,
|
|
37
|
+
});
|
|
38
|
+
const branch = branchResult.stdout.trim();
|
|
39
|
+
// Get status porcelain for parsing
|
|
40
|
+
const statusResult = await execAsync('git status --porcelain', {
|
|
41
|
+
cwd: this.workingDir,
|
|
42
|
+
timeout: 5000,
|
|
43
|
+
});
|
|
44
|
+
const staged = [];
|
|
45
|
+
const unstaged = [];
|
|
46
|
+
const untracked = [];
|
|
47
|
+
const lines = statusResult.stdout.trim().split('\n').filter(l => l);
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
const status = line.substring(0, 2);
|
|
50
|
+
const file = line.substring(3);
|
|
51
|
+
if (status[0] !== ' ' && status[0] !== '?') {
|
|
52
|
+
staged.push(file);
|
|
53
|
+
}
|
|
54
|
+
if (status[1] !== ' ' && status[1] !== '?') {
|
|
55
|
+
unstaged.push(file);
|
|
56
|
+
}
|
|
57
|
+
if (status === '??') {
|
|
58
|
+
untracked.push(file);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const isClean = lines.length === 0;
|
|
62
|
+
// Get ahead/behind info
|
|
63
|
+
let ahead = 0;
|
|
64
|
+
let behind = 0;
|
|
65
|
+
try {
|
|
66
|
+
const revListResult = await execAsync('git rev-list --left-right --count HEAD...@{upstream}', {
|
|
67
|
+
cwd: this.workingDir,
|
|
68
|
+
timeout: 5000,
|
|
69
|
+
});
|
|
70
|
+
const [aheadStr, behindStr] = revListResult.stdout.trim().split('\t');
|
|
71
|
+
ahead = parseInt(aheadStr, 10) || 0;
|
|
72
|
+
behind = parseInt(behindStr, 10) || 0;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// No upstream or other error, ignore
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
isGitRepo: true,
|
|
79
|
+
branch,
|
|
80
|
+
isClean,
|
|
81
|
+
staged,
|
|
82
|
+
unstaged,
|
|
83
|
+
untracked,
|
|
84
|
+
ahead,
|
|
85
|
+
behind,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return {
|
|
90
|
+
isGitRepo: true,
|
|
91
|
+
branch: undefined,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get list of changed files (staged + unstaged)
|
|
97
|
+
*/
|
|
98
|
+
async getChangedFiles() {
|
|
99
|
+
const status = await this.getStatus();
|
|
100
|
+
if (!status.isGitRepo)
|
|
101
|
+
return [];
|
|
102
|
+
const changed = new Set();
|
|
103
|
+
status.staged?.forEach(f => changed.add(f));
|
|
104
|
+
status.unstaged?.forEach(f => changed.add(f));
|
|
105
|
+
return Array.from(changed);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get diff summary with line count statistics
|
|
109
|
+
*/
|
|
110
|
+
async getDiffSummary(target = 'HEAD') {
|
|
111
|
+
const isRepo = await this.isGitRepository();
|
|
112
|
+
if (!isRepo) {
|
|
113
|
+
return { files: [], totalInsertions: 0, totalDeletions: 0, totalFiles: 0 };
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const { stdout } = await execAsync(`git diff --numstat ${target}`, {
|
|
117
|
+
cwd: this.workingDir,
|
|
118
|
+
timeout: 10000,
|
|
119
|
+
});
|
|
120
|
+
const files = [];
|
|
121
|
+
let totalInsertions = 0;
|
|
122
|
+
let totalDeletions = 0;
|
|
123
|
+
const lines = stdout.trim().split('\n').filter(l => l);
|
|
124
|
+
for (const line of lines) {
|
|
125
|
+
const [insertions, deletions, filepath] = line.split('\t');
|
|
126
|
+
const ins = parseInt(insertions, 10) || 0;
|
|
127
|
+
const del = parseInt(deletions, 10) || 0;
|
|
128
|
+
files.push({
|
|
129
|
+
path: filepath,
|
|
130
|
+
insertions: ins,
|
|
131
|
+
deletions: del,
|
|
132
|
+
changes: ins + del,
|
|
133
|
+
});
|
|
134
|
+
totalInsertions += ins;
|
|
135
|
+
totalDeletions += del;
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
files,
|
|
139
|
+
totalInsertions,
|
|
140
|
+
totalDeletions,
|
|
141
|
+
totalFiles: files.length,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return { files: [], totalInsertions: 0, totalDeletions: 0, totalFiles: 0 };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get unified diff for a file or entire repo
|
|
150
|
+
*/
|
|
151
|
+
async getUnifiedDiff(filePath, target = 'HEAD') {
|
|
152
|
+
const isRepo = await this.isGitRepository();
|
|
153
|
+
if (!isRepo)
|
|
154
|
+
return '';
|
|
155
|
+
try {
|
|
156
|
+
const command = filePath
|
|
157
|
+
? `git diff ${target} -- ${filePath}`
|
|
158
|
+
: `git diff ${target}`;
|
|
159
|
+
const { stdout } = await execAsync(command, {
|
|
160
|
+
cwd: this.workingDir,
|
|
161
|
+
timeout: 10000,
|
|
162
|
+
maxBuffer: 1024 * 1024 * 10,
|
|
163
|
+
});
|
|
164
|
+
return stdout;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
return '';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Create a git checkpoint (stash or commit)
|
|
172
|
+
*/
|
|
173
|
+
async createCheckpoint(message, strategy = 'stash') {
|
|
174
|
+
const isRepo = await this.isGitRepository();
|
|
175
|
+
if (!isRepo) {
|
|
176
|
+
return { success: false, error: 'Not a git repository' };
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const timestamp = new Date();
|
|
180
|
+
const fullMessage = `xibecode checkpoint: ${message}`;
|
|
181
|
+
if (strategy === 'stash') {
|
|
182
|
+
// Create a stash with keep-index to preserve staged files
|
|
183
|
+
const { stdout } = await execAsync(`git stash push -u -m "${fullMessage}"`, {
|
|
184
|
+
cwd: this.workingDir,
|
|
185
|
+
timeout: 30000,
|
|
186
|
+
});
|
|
187
|
+
// Check if stash was created (git stash says "No local changes" if nothing to stash)
|
|
188
|
+
if (stdout.includes('No local changes')) {
|
|
189
|
+
return { success: false, error: 'No changes to checkpoint' };
|
|
190
|
+
}
|
|
191
|
+
// Get the stash id
|
|
192
|
+
const stashList = await execAsync('git stash list', {
|
|
193
|
+
cwd: this.workingDir,
|
|
194
|
+
timeout: 5000,
|
|
195
|
+
});
|
|
196
|
+
const stashId = stashList.stdout.split('\n')[0]?.split(':')[0] || 'stash@{0}';
|
|
197
|
+
return {
|
|
198
|
+
success: true,
|
|
199
|
+
checkpoint: {
|
|
200
|
+
type: 'stash',
|
|
201
|
+
id: stashId,
|
|
202
|
+
message: fullMessage,
|
|
203
|
+
timestamp,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
// Create a commit with --no-verify to skip hooks
|
|
209
|
+
// First add all changes
|
|
210
|
+
await execAsync('git add -A', {
|
|
211
|
+
cwd: this.workingDir,
|
|
212
|
+
timeout: 10000,
|
|
213
|
+
});
|
|
214
|
+
const { stdout } = await execAsync(`git commit --no-verify -m "${fullMessage}"`, {
|
|
215
|
+
cwd: this.workingDir,
|
|
216
|
+
timeout: 30000,
|
|
217
|
+
});
|
|
218
|
+
// Get the commit hash
|
|
219
|
+
const hashResult = await execAsync('git rev-parse HEAD', {
|
|
220
|
+
cwd: this.workingDir,
|
|
221
|
+
timeout: 5000,
|
|
222
|
+
});
|
|
223
|
+
const commitHash = hashResult.stdout.trim();
|
|
224
|
+
return {
|
|
225
|
+
success: true,
|
|
226
|
+
checkpoint: {
|
|
227
|
+
type: 'commit',
|
|
228
|
+
id: commitHash,
|
|
229
|
+
message: fullMessage,
|
|
230
|
+
timestamp,
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
return { success: false, error: error.message };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Revert to a git checkpoint
|
|
241
|
+
*/
|
|
242
|
+
async revertToCheckpoint(checkpoint, confirm = false) {
|
|
243
|
+
if (!confirm) {
|
|
244
|
+
return { success: false, error: 'Revert requires explicit confirmation. Set confirm: true' };
|
|
245
|
+
}
|
|
246
|
+
const isRepo = await this.isGitRepository();
|
|
247
|
+
if (!isRepo) {
|
|
248
|
+
return { success: false, error: 'Not a git repository' };
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
if (checkpoint.type === 'stash') {
|
|
252
|
+
// Apply the stash
|
|
253
|
+
await execAsync(`git stash apply ${checkpoint.id}`, {
|
|
254
|
+
cwd: this.workingDir,
|
|
255
|
+
timeout: 30000,
|
|
256
|
+
});
|
|
257
|
+
return { success: true };
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// Reset to commit (soft reset to preserve changes as uncommitted)
|
|
261
|
+
await execAsync(`git reset --soft ${checkpoint.id}`, {
|
|
262
|
+
cwd: this.workingDir,
|
|
263
|
+
timeout: 10000,
|
|
264
|
+
});
|
|
265
|
+
return { success: true };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
return { success: false, error: error.message };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* List recent checkpoints created by XibeCode
|
|
274
|
+
*/
|
|
275
|
+
async listCheckpoints(limit = 10) {
|
|
276
|
+
const isRepo = await this.isGitRepository();
|
|
277
|
+
if (!isRepo)
|
|
278
|
+
return [];
|
|
279
|
+
const checkpoints = [];
|
|
280
|
+
try {
|
|
281
|
+
// Get stashes
|
|
282
|
+
const { stdout: stashList } = await execAsync('git stash list', {
|
|
283
|
+
cwd: this.workingDir,
|
|
284
|
+
timeout: 5000,
|
|
285
|
+
});
|
|
286
|
+
const stashLines = stashList.trim().split('\n').filter(l => l);
|
|
287
|
+
for (const line of stashLines) {
|
|
288
|
+
if (line.includes('xibecode checkpoint')) {
|
|
289
|
+
const match = line.match(/^(stash@\{(\d+)\}): (.+): (.+)$/);
|
|
290
|
+
if (match) {
|
|
291
|
+
const [, id, , , message] = match;
|
|
292
|
+
checkpoints.push({
|
|
293
|
+
type: 'stash',
|
|
294
|
+
id,
|
|
295
|
+
message,
|
|
296
|
+
timestamp: new Date(), // Git stash doesn't expose timestamp easily
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Get commits
|
|
302
|
+
const { stdout: commitList } = await execAsync(`git log -${limit} --pretty=format:"%H|%s|%cd" --date=iso`, {
|
|
303
|
+
cwd: this.workingDir,
|
|
304
|
+
timeout: 5000,
|
|
305
|
+
});
|
|
306
|
+
const commitLines = commitList.trim().split('\n').filter(l => l);
|
|
307
|
+
for (const line of commitLines) {
|
|
308
|
+
const [hash, message, dateStr] = line.split('|');
|
|
309
|
+
if (message.includes('xibecode checkpoint')) {
|
|
310
|
+
checkpoints.push({
|
|
311
|
+
type: 'commit',
|
|
312
|
+
id: hash,
|
|
313
|
+
message,
|
|
314
|
+
timestamp: new Date(dateStr),
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
// Ignore errors
|
|
321
|
+
}
|
|
322
|
+
return checkpoints.slice(0, limit);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Get files changed compared to a specific branch or commit
|
|
326
|
+
*/
|
|
327
|
+
async getChangedFilesSince(target) {
|
|
328
|
+
const isRepo = await this.isGitRepository();
|
|
329
|
+
if (!isRepo)
|
|
330
|
+
return [];
|
|
331
|
+
try {
|
|
332
|
+
const { stdout } = await execAsync(`git diff --name-only ${target}`, {
|
|
333
|
+
cwd: this.workingDir,
|
|
334
|
+
timeout: 10000,
|
|
335
|
+
});
|
|
336
|
+
return stdout.trim().split('\n').filter(l => l);
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
return [];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety utilities for classifying and managing risky operations
|
|
3
|
+
*/
|
|
4
|
+
export type RiskLevel = 'low' | 'medium' | 'high';
|
|
5
|
+
export interface RiskAssessment {
|
|
6
|
+
level: RiskLevel;
|
|
7
|
+
reasons: string[];
|
|
8
|
+
warnings: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare class SafetyChecker {
|
|
11
|
+
/**
|
|
12
|
+
* Assess the risk level of a tool operation
|
|
13
|
+
*/
|
|
14
|
+
assessToolRisk(toolName: string, params: any): RiskAssessment;
|
|
15
|
+
/**
|
|
16
|
+
* Assess the risk level of a shell command
|
|
17
|
+
*/
|
|
18
|
+
assessCommandRisk(command: string): RiskLevel;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a command should be blocked entirely
|
|
21
|
+
*/
|
|
22
|
+
isCommandBlocked(command: string): {
|
|
23
|
+
blocked: boolean;
|
|
24
|
+
reason?: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Suggest safer alternatives for risky commands
|
|
28
|
+
*/
|
|
29
|
+
suggestSaferAlternative(command: string): string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a file path is sensitive
|
|
32
|
+
*/
|
|
33
|
+
isSensitivePath(filepath: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Validate dry-run compatibility for a tool
|
|
36
|
+
*/
|
|
37
|
+
canDryRun(toolName: string): boolean;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety.d.ts","sourceRoot":"","sources":["../../src/utils/safety.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,qBAAa,aAAa;IACxB;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,cAAc;IAqG7D;;OAEG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS;IAkD7C;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAqBxE;;OAEG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAsBvD;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAgB1C;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAgBrC"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety utilities for classifying and managing risky operations
|
|
3
|
+
*/
|
|
4
|
+
export class SafetyChecker {
|
|
5
|
+
/**
|
|
6
|
+
* Assess the risk level of a tool operation
|
|
7
|
+
*/
|
|
8
|
+
assessToolRisk(toolName, params) {
|
|
9
|
+
const reasons = [];
|
|
10
|
+
const warnings = [];
|
|
11
|
+
let level = 'low';
|
|
12
|
+
switch (toolName) {
|
|
13
|
+
case 'delete_file':
|
|
14
|
+
level = 'high';
|
|
15
|
+
reasons.push('Deletes files/directories permanently');
|
|
16
|
+
warnings.push('Ensure backups exist before deletion');
|
|
17
|
+
// Check if deleting important directories
|
|
18
|
+
if (params.path) {
|
|
19
|
+
const dangerousPaths = [
|
|
20
|
+
'node_modules',
|
|
21
|
+
'.git',
|
|
22
|
+
'dist',
|
|
23
|
+
'build',
|
|
24
|
+
'src',
|
|
25
|
+
'/',
|
|
26
|
+
'~',
|
|
27
|
+
];
|
|
28
|
+
for (const dangerous of dangerousPaths) {
|
|
29
|
+
if (params.path.includes(dangerous)) {
|
|
30
|
+
level = 'high';
|
|
31
|
+
warnings.push(`Deleting ${dangerous} directory - HIGH RISK`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
break;
|
|
36
|
+
case 'write_file':
|
|
37
|
+
// Writing is generally safe but can overwrite
|
|
38
|
+
if (params.path) {
|
|
39
|
+
// Check for important files
|
|
40
|
+
const importantFiles = [
|
|
41
|
+
'package.json',
|
|
42
|
+
'tsconfig.json',
|
|
43
|
+
'.gitignore',
|
|
44
|
+
'README.md',
|
|
45
|
+
'.env',
|
|
46
|
+
];
|
|
47
|
+
if (importantFiles.some(f => params.path.endsWith(f))) {
|
|
48
|
+
level = 'medium';
|
|
49
|
+
reasons.push('Modifying important configuration file');
|
|
50
|
+
warnings.push('Verify changes to avoid breaking the project');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
case 'edit_file':
|
|
55
|
+
case 'edit_lines':
|
|
56
|
+
case 'insert_at_line':
|
|
57
|
+
level = 'low';
|
|
58
|
+
reasons.push('File edits are backed up automatically');
|
|
59
|
+
break;
|
|
60
|
+
case 'move_file':
|
|
61
|
+
level = 'medium';
|
|
62
|
+
reasons.push('Moving files can break imports and references');
|
|
63
|
+
warnings.push('Update imports after moving files');
|
|
64
|
+
break;
|
|
65
|
+
case 'run_command':
|
|
66
|
+
level = this.assessCommandRisk(params.command);
|
|
67
|
+
if (level === 'high') {
|
|
68
|
+
reasons.push('Command can make irreversible changes');
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
case 'create_git_checkpoint':
|
|
72
|
+
level = 'low';
|
|
73
|
+
reasons.push('Creates a safe restore point');
|
|
74
|
+
break;
|
|
75
|
+
case 'revert_to_git_checkpoint':
|
|
76
|
+
level = 'high';
|
|
77
|
+
reasons.push('Reverts code changes');
|
|
78
|
+
warnings.push('Ensure correct checkpoint before reverting');
|
|
79
|
+
if (!params.confirm) {
|
|
80
|
+
warnings.push('Requires explicit confirmation');
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
case 'revert_file':
|
|
84
|
+
level = 'medium';
|
|
85
|
+
reasons.push('Reverts file to previous version');
|
|
86
|
+
warnings.push('Verify backup index before reverting');
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
level = 'low';
|
|
90
|
+
}
|
|
91
|
+
return { level, reasons, warnings };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Assess the risk level of a shell command
|
|
95
|
+
*/
|
|
96
|
+
assessCommandRisk(command) {
|
|
97
|
+
const cmd = command.toLowerCase().trim();
|
|
98
|
+
// High-risk commands
|
|
99
|
+
const highRiskPatterns = [
|
|
100
|
+
/rm\s+-rf/,
|
|
101
|
+
/git\s+reset\s+--hard/,
|
|
102
|
+
/git\s+push\s+--force/,
|
|
103
|
+
/git\s+push\s+-f/,
|
|
104
|
+
/dd\s+if=/,
|
|
105
|
+
/mkfs/,
|
|
106
|
+
/format/,
|
|
107
|
+
/:\(\)\{.*\}/, // Fork bomb pattern
|
|
108
|
+
/chmod\s+777/,
|
|
109
|
+
/chown\s+-R/,
|
|
110
|
+
/sudo\s+rm/,
|
|
111
|
+
/> \/dev\//,
|
|
112
|
+
/curl.*\|\s*(bash|sh)/,
|
|
113
|
+
/wget.*\|\s*(bash|sh)/,
|
|
114
|
+
];
|
|
115
|
+
for (const pattern of highRiskPatterns) {
|
|
116
|
+
if (pattern.test(cmd)) {
|
|
117
|
+
return 'high';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Medium-risk commands
|
|
121
|
+
const mediumRiskPatterns = [
|
|
122
|
+
/git\s+push/,
|
|
123
|
+
/npm\s+publish/,
|
|
124
|
+
/rm\s+/,
|
|
125
|
+
/git\s+reset/,
|
|
126
|
+
/git\s+rebase/,
|
|
127
|
+
/git\s+merge/,
|
|
128
|
+
/docker\s+rm/,
|
|
129
|
+
/docker\s+rmi/,
|
|
130
|
+
/kill\s+-9/,
|
|
131
|
+
/pkill/,
|
|
132
|
+
];
|
|
133
|
+
for (const pattern of mediumRiskPatterns) {
|
|
134
|
+
if (pattern.test(cmd)) {
|
|
135
|
+
return 'medium';
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return 'low';
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Check if a command should be blocked entirely
|
|
142
|
+
*/
|
|
143
|
+
isCommandBlocked(command) {
|
|
144
|
+
const cmd = command.toLowerCase().trim();
|
|
145
|
+
// Block extremely dangerous commands
|
|
146
|
+
const blockedPatterns = [
|
|
147
|
+
{ pattern: /:\(\)\{.*\}/, reason: 'Fork bomb detected' },
|
|
148
|
+
{ pattern: /rm\s+-rf\s+\/($|\s)/, reason: 'Attempting to delete root directory' },
|
|
149
|
+
{ pattern: /rm\s+-rf\s+~($|\s)/, reason: 'Attempting to delete home directory' },
|
|
150
|
+
{ pattern: /> \/dev\/sda/, reason: 'Attempting to write directly to disk' },
|
|
151
|
+
{ pattern: /mkfs/, reason: 'Attempting to format filesystem' },
|
|
152
|
+
];
|
|
153
|
+
for (const { pattern, reason } of blockedPatterns) {
|
|
154
|
+
if (pattern.test(cmd)) {
|
|
155
|
+
return { blocked: true, reason };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { blocked: false };
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Suggest safer alternatives for risky commands
|
|
162
|
+
*/
|
|
163
|
+
suggestSaferAlternative(command) {
|
|
164
|
+
const cmd = command.toLowerCase().trim();
|
|
165
|
+
if (cmd.includes('git reset --hard')) {
|
|
166
|
+
return 'Use "git stash" to save changes, then "git stash drop" if you really want to discard them';
|
|
167
|
+
}
|
|
168
|
+
if (cmd.includes('git push --force') || cmd.includes('git push -f')) {
|
|
169
|
+
return 'Use "git push --force-with-lease" to avoid overwriting others\' work';
|
|
170
|
+
}
|
|
171
|
+
if (cmd.match(/rm\s+-rf/)) {
|
|
172
|
+
return 'Consider using "mv" to move files to a temporary location first';
|
|
173
|
+
}
|
|
174
|
+
if (cmd.includes('npm install') && !cmd.includes('-g')) {
|
|
175
|
+
return 'Consider using "pnpm install" or "bun install" for faster, more efficient installs';
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Check if a file path is sensitive
|
|
181
|
+
*/
|
|
182
|
+
isSensitivePath(filepath) {
|
|
183
|
+
const sensitive = [
|
|
184
|
+
'.env',
|
|
185
|
+
'.env.local',
|
|
186
|
+
'.env.production',
|
|
187
|
+
'credentials.json',
|
|
188
|
+
'secrets.yaml',
|
|
189
|
+
'private.key',
|
|
190
|
+
'.ssh/',
|
|
191
|
+
'id_rsa',
|
|
192
|
+
'id_ed25519',
|
|
193
|
+
];
|
|
194
|
+
return sensitive.some(pattern => filepath.includes(pattern));
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Validate dry-run compatibility for a tool
|
|
198
|
+
*/
|
|
199
|
+
canDryRun(toolName) {
|
|
200
|
+
const dryRunnable = [
|
|
201
|
+
'write_file',
|
|
202
|
+
'edit_file',
|
|
203
|
+
'edit_lines',
|
|
204
|
+
'insert_at_line',
|
|
205
|
+
'delete_file',
|
|
206
|
+
'move_file',
|
|
207
|
+
'create_directory',
|
|
208
|
+
'create_git_checkpoint',
|
|
209
|
+
'revert_to_git_checkpoint',
|
|
210
|
+
'revert_file',
|
|
211
|
+
];
|
|
212
|
+
return dryRunnable.includes(toolName);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/utils/safety.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface TestRunnerInfo {
|
|
2
|
+
detected: boolean;
|
|
3
|
+
runner?: string;
|
|
4
|
+
command?: string;
|
|
5
|
+
packageManager?: 'pnpm' | 'bun' | 'npm';
|
|
6
|
+
scriptName?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface TestResult {
|
|
9
|
+
success: boolean;
|
|
10
|
+
exitCode?: number;
|
|
11
|
+
output: string;
|
|
12
|
+
errors: string;
|
|
13
|
+
duration?: number;
|
|
14
|
+
testsRun?: number;
|
|
15
|
+
testsPassed?: number;
|
|
16
|
+
testsFailed?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare class TestRunnerDetector {
|
|
19
|
+
private workingDir;
|
|
20
|
+
constructor(workingDir?: string);
|
|
21
|
+
/**
|
|
22
|
+
* Detect which package manager is available
|
|
23
|
+
* Priority: pnpm > bun > npm (as per user preference)
|
|
24
|
+
*/
|
|
25
|
+
detectPackageManager(): Promise<'pnpm' | 'bun' | 'npm'>;
|
|
26
|
+
/**
|
|
27
|
+
* Detect test runner and command
|
|
28
|
+
*/
|
|
29
|
+
detectTestRunner(customCommand?: string): Promise<TestRunnerInfo>;
|
|
30
|
+
/**
|
|
31
|
+
* Detect test runner from package.json dependencies
|
|
32
|
+
*/
|
|
33
|
+
private detectRunnerFromDeps;
|
|
34
|
+
/**
|
|
35
|
+
* Parse test output to extract statistics
|
|
36
|
+
*/
|
|
37
|
+
parseTestOutput(output: string, runner?: string): Partial<TestResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Extract failure details from test output
|
|
40
|
+
*/
|
|
41
|
+
extractFailures(output: string): string[];
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=testRunner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testRunner.d.ts","sourceRoot":"","sources":["../../src/utils/testRunner.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,GAAE,MAAsB;IAI9C;;;OAGG;IACG,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IAiC7D;;OAEG;IACG,gBAAgB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA+FvE;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAgB5B;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAkErE;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;CAiD1C"}
|