codebot-ai 1.3.0 → 1.4.1
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 +16 -1
- package/dist/agent.js +23 -1
- package/dist/cli.js +1 -1
- package/dist/providers/anthropic.js +27 -1
- package/dist/providers/openai.d.ts +4 -0
- package/dist/providers/openai.js +54 -2
- package/dist/retry.d.ts +5 -0
- package/dist/retry.js +24 -0
- package/dist/tools/code-analysis.d.ts +33 -0
- package/dist/tools/code-analysis.js +232 -0
- package/dist/tools/code-review.d.ts +32 -0
- package/dist/tools/code-review.js +228 -0
- package/dist/tools/database.d.ts +35 -0
- package/dist/tools/database.js +129 -0
- package/dist/tools/diff-viewer.d.ts +39 -0
- package/dist/tools/diff-viewer.js +145 -0
- package/dist/tools/docker.d.ts +26 -0
- package/dist/tools/docker.js +101 -0
- package/dist/tools/git.d.ts +26 -0
- package/dist/tools/git.js +58 -0
- package/dist/tools/http-client.d.ts +39 -0
- package/dist/tools/http-client.js +114 -0
- package/dist/tools/image-info.d.ts +23 -0
- package/dist/tools/image-info.js +170 -0
- package/dist/tools/index.js +34 -0
- package/dist/tools/multi-search.d.ts +28 -0
- package/dist/tools/multi-search.js +153 -0
- package/dist/tools/notification.d.ts +38 -0
- package/dist/tools/notification.js +96 -0
- package/dist/tools/package-manager.d.ts +31 -0
- package/dist/tools/package-manager.js +161 -0
- package/dist/tools/pdf-extract.d.ts +33 -0
- package/dist/tools/pdf-extract.js +178 -0
- package/dist/tools/ssh-remote.d.ts +39 -0
- package/dist/tools/ssh-remote.js +84 -0
- package/dist/tools/task-planner.d.ts +42 -0
- package/dist/tools/task-planner.js +161 -0
- package/dist/tools/test-runner.d.ts +36 -0
- package/dist/tools/test-runner.js +193 -0
- package/package.json +1 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CodeReviewTool = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const SECURITY_PATTERNS = [
|
|
40
|
+
{ pattern: /\beval\s*\(/, rule: 'no-eval', message: 'eval() is a security risk — allows arbitrary code execution', severity: 'error' },
|
|
41
|
+
{ pattern: /new\s+Function\s*\(/, rule: 'no-new-function', message: 'new Function() is equivalent to eval()', severity: 'error' },
|
|
42
|
+
{ pattern: /child_process.*exec(?!Sync)/, rule: 'unsafe-exec', message: 'exec() can be vulnerable to command injection — prefer execFile()', severity: 'warning' },
|
|
43
|
+
{ pattern: /innerHTML\s*=/, rule: 'no-innerhtml', message: 'innerHTML is vulnerable to XSS attacks', severity: 'warning' },
|
|
44
|
+
{ pattern: /document\.write\s*\(/, rule: 'no-document-write', message: 'document.write() is a security and performance issue', severity: 'warning' },
|
|
45
|
+
{ pattern: /(?:password|secret|api.?key|token)\s*[:=]\s*['"][^'"]{8,}['"]/i, rule: 'hardcoded-secret', message: 'Possible hardcoded secret/credential', severity: 'error' },
|
|
46
|
+
{ pattern: /\bsqlite3?\s.*\+\s*(?:req\.|args\.|input)/i, rule: 'sql-injection', message: 'Possible SQL injection — use parameterized queries', severity: 'error' },
|
|
47
|
+
{ pattern: /https?:\/\/[^'"]*['"]\s*\+/, rule: 'url-injection', message: 'String concatenation in URL — possible injection', severity: 'warning' },
|
|
48
|
+
{ pattern: /console\.(log|debug|info)\(/, rule: 'no-console', message: 'Console statement (consider removing for production)', severity: 'info' },
|
|
49
|
+
{ pattern: /TODO|FIXME|HACK|XXX/i, rule: 'todo-comment', message: 'TODO/FIXME comment found', severity: 'info' },
|
|
50
|
+
];
|
|
51
|
+
class CodeReviewTool {
|
|
52
|
+
name = 'code_review';
|
|
53
|
+
description = 'Review code for security issues, complexity, and code smells. Actions: security, complexity, review (full).';
|
|
54
|
+
permission = 'auto';
|
|
55
|
+
parameters = {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
action: { type: 'string', description: 'Action: security (scan for vulnerabilities), complexity (function/nesting analysis), review (full review)' },
|
|
59
|
+
path: { type: 'string', description: 'File or directory to review' },
|
|
60
|
+
severity: { type: 'string', description: 'Minimum severity to report: error, warning, info (default: warning)' },
|
|
61
|
+
},
|
|
62
|
+
required: ['action', 'path'],
|
|
63
|
+
};
|
|
64
|
+
async execute(args) {
|
|
65
|
+
const action = args.action;
|
|
66
|
+
const targetPath = args.path;
|
|
67
|
+
if (!action)
|
|
68
|
+
return 'Error: action is required';
|
|
69
|
+
if (!targetPath)
|
|
70
|
+
return 'Error: path is required';
|
|
71
|
+
if (!fs.existsSync(targetPath))
|
|
72
|
+
return `Error: path not found: ${targetPath}`;
|
|
73
|
+
const minSeverity = args.severity || 'warning';
|
|
74
|
+
switch (action) {
|
|
75
|
+
case 'security': return this.securityScan(targetPath, minSeverity);
|
|
76
|
+
case 'complexity': return this.complexityAnalysis(targetPath);
|
|
77
|
+
case 'review': {
|
|
78
|
+
const sec = this.securityScan(targetPath, minSeverity);
|
|
79
|
+
const comp = this.complexityAnalysis(targetPath);
|
|
80
|
+
return `=== Security Review ===\n${sec}\n\n=== Complexity Analysis ===\n${comp}`;
|
|
81
|
+
}
|
|
82
|
+
default: return `Error: unknown action "${action}". Use: security, complexity, review`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
securityScan(targetPath, minSeverity) {
|
|
86
|
+
const issues = [];
|
|
87
|
+
const sevOrder = { error: 3, warning: 2, info: 1 };
|
|
88
|
+
const minLevel = sevOrder[minSeverity] || 2;
|
|
89
|
+
const stat = fs.statSync(targetPath);
|
|
90
|
+
if (stat.isFile()) {
|
|
91
|
+
this.scanFile(targetPath, issues);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
this.scanDir(targetPath, issues);
|
|
95
|
+
}
|
|
96
|
+
// Filter by severity
|
|
97
|
+
const filtered = issues.filter(i => (sevOrder[i.severity] || 0) >= minLevel);
|
|
98
|
+
if (filtered.length === 0)
|
|
99
|
+
return 'No security issues found.';
|
|
100
|
+
const errors = filtered.filter(i => i.severity === 'error').length;
|
|
101
|
+
const warnings = filtered.filter(i => i.severity === 'warning').length;
|
|
102
|
+
const infos = filtered.filter(i => i.severity === 'info').length;
|
|
103
|
+
const icons = { error: 'X', warning: '!', info: 'i' };
|
|
104
|
+
const lines = filtered.slice(0, 50).map(i => ` [${icons[i.severity]}] ${i.file}:${i.line} ${i.rule} — ${i.message}`);
|
|
105
|
+
return `Found ${filtered.length} issue(s): ${errors} errors, ${warnings} warnings, ${infos} info\n${lines.join('\n')}`;
|
|
106
|
+
}
|
|
107
|
+
complexityAnalysis(targetPath) {
|
|
108
|
+
const stat = fs.statSync(targetPath);
|
|
109
|
+
const results = [];
|
|
110
|
+
if (stat.isFile()) {
|
|
111
|
+
this.analyzeFileComplexity(targetPath, results);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
this.analyzeDir(targetPath, results);
|
|
115
|
+
}
|
|
116
|
+
if (results.length === 0)
|
|
117
|
+
return 'No complexity issues found.';
|
|
118
|
+
return results.join('\n');
|
|
119
|
+
}
|
|
120
|
+
scanFile(filePath, issues) {
|
|
121
|
+
const ext = path.extname(filePath);
|
|
122
|
+
if (!['.ts', '.js', '.tsx', '.jsx', '.py', '.go', '.rb', '.java'].includes(ext))
|
|
123
|
+
return;
|
|
124
|
+
try {
|
|
125
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
126
|
+
const lines = content.split('\n');
|
|
127
|
+
for (let i = 0; i < lines.length; i++) {
|
|
128
|
+
for (const check of SECURITY_PATTERNS) {
|
|
129
|
+
if (check.pattern.test(lines[i])) {
|
|
130
|
+
issues.push({
|
|
131
|
+
file: filePath, line: i + 1,
|
|
132
|
+
severity: check.severity, rule: check.rule, message: check.message,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch { /* skip */ }
|
|
139
|
+
}
|
|
140
|
+
scanDir(dir, issues) {
|
|
141
|
+
const skip = new Set(['node_modules', '.git', 'dist', 'build', 'coverage', '__pycache__']);
|
|
142
|
+
let entries;
|
|
143
|
+
try {
|
|
144
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
for (const entry of entries) {
|
|
150
|
+
if (entry.name.startsWith('.') || skip.has(entry.name))
|
|
151
|
+
continue;
|
|
152
|
+
const full = path.join(dir, entry.name);
|
|
153
|
+
if (entry.isDirectory()) {
|
|
154
|
+
this.scanDir(full, issues);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this.scanFile(full, issues);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
analyzeFileComplexity(filePath, results) {
|
|
162
|
+
const ext = path.extname(filePath);
|
|
163
|
+
if (!['.ts', '.js', '.tsx', '.jsx', '.py', '.go'].includes(ext))
|
|
164
|
+
return;
|
|
165
|
+
try {
|
|
166
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
167
|
+
const lines = content.split('\n');
|
|
168
|
+
let currentFunc = '';
|
|
169
|
+
let funcStart = 0;
|
|
170
|
+
let maxNesting = 0;
|
|
171
|
+
let currentNesting = 0;
|
|
172
|
+
for (let i = 0; i < lines.length; i++) {
|
|
173
|
+
const line = lines[i];
|
|
174
|
+
// Detect function starts
|
|
175
|
+
const funcMatch = line.match(/(?:function|async function|def|fn|func)\s+(\w+)|(\w+)\s*[:=]\s*(?:async\s+)?\(/);
|
|
176
|
+
if (funcMatch) {
|
|
177
|
+
// Report previous function if long
|
|
178
|
+
if (currentFunc && (i - funcStart) > 50) {
|
|
179
|
+
results.push(` [!] ${filePath}:${funcStart + 1} "${currentFunc}" is ${i - funcStart} lines long (consider breaking up)`);
|
|
180
|
+
}
|
|
181
|
+
currentFunc = funcMatch[1] || funcMatch[2] || '';
|
|
182
|
+
funcStart = i;
|
|
183
|
+
maxNesting = 0;
|
|
184
|
+
}
|
|
185
|
+
// Track nesting
|
|
186
|
+
const opens = (line.match(/[{(]/g) || []).length;
|
|
187
|
+
const closes = (line.match(/[})]/g) || []).length;
|
|
188
|
+
currentNesting += opens - closes;
|
|
189
|
+
if (currentNesting > maxNesting)
|
|
190
|
+
maxNesting = currentNesting;
|
|
191
|
+
if (maxNesting > 5 && currentFunc) {
|
|
192
|
+
results.push(` [!] ${filePath}:${i + 1} deep nesting (${maxNesting} levels) in "${currentFunc}"`);
|
|
193
|
+
maxNesting = 0; // Don't re-report
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Check last function
|
|
197
|
+
if (currentFunc && (lines.length - funcStart) > 50) {
|
|
198
|
+
results.push(` [!] ${filePath}:${funcStart + 1} "${currentFunc}" is ${lines.length - funcStart} lines long`);
|
|
199
|
+
}
|
|
200
|
+
// File-level checks
|
|
201
|
+
if (lines.length > 500) {
|
|
202
|
+
results.push(` [i] ${filePath}: ${lines.length} lines — consider splitting into modules`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch { /* skip */ }
|
|
206
|
+
}
|
|
207
|
+
analyzeDir(dir, results) {
|
|
208
|
+
const skip = new Set(['node_modules', '.git', 'dist', 'build', 'coverage']);
|
|
209
|
+
let entries;
|
|
210
|
+
try {
|
|
211
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
for (const entry of entries) {
|
|
217
|
+
if (entry.name.startsWith('.') || skip.has(entry.name))
|
|
218
|
+
continue;
|
|
219
|
+
const full = path.join(dir, entry.name);
|
|
220
|
+
if (entry.isDirectory())
|
|
221
|
+
this.analyzeDir(full, results);
|
|
222
|
+
else
|
|
223
|
+
this.analyzeFileComplexity(full, results);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
exports.CodeReviewTool = CodeReviewTool;
|
|
228
|
+
//# sourceMappingURL=code-review.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Tool } from '../types';
|
|
2
|
+
export declare class DatabaseTool implements Tool {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
permission: Tool['permission'];
|
|
6
|
+
parameters: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
action: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
db: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
sql: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
table: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
required: string[];
|
|
27
|
+
};
|
|
28
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
29
|
+
private runQuery;
|
|
30
|
+
private listTables;
|
|
31
|
+
private showSchema;
|
|
32
|
+
private dbInfo;
|
|
33
|
+
private sqlite;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DatabaseTool = void 0;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const BLOCKED_SQL = [
|
|
40
|
+
/\bDROP\s+(TABLE|DATABASE|INDEX|VIEW)\b/i,
|
|
41
|
+
/\bDELETE\s+FROM\b/i,
|
|
42
|
+
/\bTRUNCATE\b/i,
|
|
43
|
+
/\bALTER\s+TABLE\s+.*\bDROP\b/i,
|
|
44
|
+
];
|
|
45
|
+
class DatabaseTool {
|
|
46
|
+
name = 'database';
|
|
47
|
+
description = 'Query SQLite databases. Actions: query, tables, schema, info. Blocks DROP/DELETE/TRUNCATE for safety.';
|
|
48
|
+
permission = 'prompt';
|
|
49
|
+
parameters = {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
action: { type: 'string', description: 'Action: query, tables, schema, info' },
|
|
53
|
+
db: { type: 'string', description: 'Path to SQLite database file' },
|
|
54
|
+
sql: { type: 'string', description: 'SQL query to execute (for "query" action)' },
|
|
55
|
+
table: { type: 'string', description: 'Table name (for "schema" action)' },
|
|
56
|
+
},
|
|
57
|
+
required: ['action', 'db'],
|
|
58
|
+
};
|
|
59
|
+
async execute(args) {
|
|
60
|
+
const action = args.action;
|
|
61
|
+
const dbPath = args.db;
|
|
62
|
+
if (!action)
|
|
63
|
+
return 'Error: action is required';
|
|
64
|
+
if (!dbPath)
|
|
65
|
+
return 'Error: db path is required';
|
|
66
|
+
if (!fs.existsSync(dbPath))
|
|
67
|
+
return `Error: database not found: ${dbPath}`;
|
|
68
|
+
switch (action) {
|
|
69
|
+
case 'query': return this.runQuery(dbPath, args);
|
|
70
|
+
case 'tables': return this.listTables(dbPath);
|
|
71
|
+
case 'schema': return this.showSchema(dbPath, args);
|
|
72
|
+
case 'info': return this.dbInfo(dbPath);
|
|
73
|
+
default: return `Error: unknown action "${action}". Use: query, tables, schema, info`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
runQuery(dbPath, args) {
|
|
77
|
+
const sql = args.sql;
|
|
78
|
+
if (!sql)
|
|
79
|
+
return 'Error: sql is required for query';
|
|
80
|
+
// Block destructive queries
|
|
81
|
+
for (const pattern of BLOCKED_SQL) {
|
|
82
|
+
if (pattern.test(sql)) {
|
|
83
|
+
return `Error: destructive SQL blocked for safety. Pattern matched: ${pattern.source}`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return this.sqlite(dbPath, sql);
|
|
87
|
+
}
|
|
88
|
+
listTables(dbPath) {
|
|
89
|
+
return this.sqlite(dbPath, ".tables");
|
|
90
|
+
}
|
|
91
|
+
showSchema(dbPath, args) {
|
|
92
|
+
const table = args.table;
|
|
93
|
+
if (table) {
|
|
94
|
+
// Sanitize table name
|
|
95
|
+
if (!/^[a-zA-Z_]\w*$/.test(table))
|
|
96
|
+
return 'Error: invalid table name';
|
|
97
|
+
return this.sqlite(dbPath, `.schema ${table}`);
|
|
98
|
+
}
|
|
99
|
+
return this.sqlite(dbPath, ".schema");
|
|
100
|
+
}
|
|
101
|
+
dbInfo(dbPath) {
|
|
102
|
+
const stat = fs.statSync(dbPath);
|
|
103
|
+
const sizeMB = (stat.size / (1024 * 1024)).toFixed(2);
|
|
104
|
+
const tables = this.sqlite(dbPath, "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;");
|
|
105
|
+
return `Database: ${dbPath}\nSize: ${sizeMB} MB\nModified: ${stat.mtime.toISOString()}\n\nTables:\n${tables}`;
|
|
106
|
+
}
|
|
107
|
+
sqlite(dbPath, command) {
|
|
108
|
+
try {
|
|
109
|
+
// Try sqlite3 CLI first
|
|
110
|
+
const output = (0, child_process_1.execSync)(`sqlite3 "${dbPath}" "${command.replace(/"/g, '\\"')}"`, {
|
|
111
|
+
timeout: 15_000,
|
|
112
|
+
maxBuffer: 1024 * 1024,
|
|
113
|
+
encoding: 'utf-8',
|
|
114
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
115
|
+
});
|
|
116
|
+
return output.trim() || '(no results)';
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
const e = err;
|
|
120
|
+
const msg = (e.stderr || '').trim();
|
|
121
|
+
if (msg.includes('not found')) {
|
|
122
|
+
return 'Error: sqlite3 is not installed. Install it with: brew install sqlite (macOS) or apt install sqlite3 (Linux)';
|
|
123
|
+
}
|
|
124
|
+
return `Error: ${msg || 'query failed'}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.DatabaseTool = DatabaseTool;
|
|
129
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Tool } from '../types';
|
|
2
|
+
export declare class DiffViewerTool implements Tool {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
permission: Tool['permission'];
|
|
6
|
+
parameters: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
action: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
file_a: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
file_b: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
path: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
ref: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
required: string[];
|
|
31
|
+
};
|
|
32
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
33
|
+
private diffFiles;
|
|
34
|
+
private gitDiff;
|
|
35
|
+
private gitStaged;
|
|
36
|
+
private gitCommitDiff;
|
|
37
|
+
private runGit;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=diff-viewer.d.ts.map
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DiffViewerTool = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const child_process_1 = require("child_process");
|
|
39
|
+
class DiffViewerTool {
|
|
40
|
+
name = 'diff_viewer';
|
|
41
|
+
description = 'View diffs. Actions: files (compare two files), git_diff (working tree changes), staged (staged changes), commit (show a commit diff).';
|
|
42
|
+
permission = 'auto';
|
|
43
|
+
parameters = {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
action: { type: 'string', description: 'Action: files, git_diff, staged, commit' },
|
|
47
|
+
file_a: { type: 'string', description: 'First file path (for "files" action)' },
|
|
48
|
+
file_b: { type: 'string', description: 'Second file path (for "files" action)' },
|
|
49
|
+
path: { type: 'string', description: 'File or directory to diff (for git_diff/staged)' },
|
|
50
|
+
ref: { type: 'string', description: 'Commit hash or ref (for "commit" action)' },
|
|
51
|
+
},
|
|
52
|
+
required: ['action'],
|
|
53
|
+
};
|
|
54
|
+
async execute(args) {
|
|
55
|
+
const action = args.action;
|
|
56
|
+
if (!action)
|
|
57
|
+
return 'Error: action is required';
|
|
58
|
+
switch (action) {
|
|
59
|
+
case 'files': return this.diffFiles(args);
|
|
60
|
+
case 'git_diff': return this.gitDiff(args);
|
|
61
|
+
case 'staged': return this.gitStaged(args);
|
|
62
|
+
case 'commit': return this.gitCommitDiff(args);
|
|
63
|
+
default: return `Error: unknown action "${action}". Use: files, git_diff, staged, commit`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
diffFiles(args) {
|
|
67
|
+
const fileA = args.file_a;
|
|
68
|
+
const fileB = args.file_b;
|
|
69
|
+
if (!fileA || !fileB)
|
|
70
|
+
return 'Error: file_a and file_b are required';
|
|
71
|
+
let contentA, contentB;
|
|
72
|
+
try {
|
|
73
|
+
contentA = fs.readFileSync(fileA, 'utf-8');
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return `Error: cannot read ${fileA}`;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
contentB = fs.readFileSync(fileB, 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return `Error: cannot read ${fileB}`;
|
|
83
|
+
}
|
|
84
|
+
const linesA = contentA.split('\n');
|
|
85
|
+
const linesB = contentB.split('\n');
|
|
86
|
+
const diff = [`--- ${fileA}`, `+++ ${fileB}`];
|
|
87
|
+
// Simple line-by-line diff
|
|
88
|
+
const maxLen = Math.max(linesA.length, linesB.length);
|
|
89
|
+
let changes = 0;
|
|
90
|
+
for (let i = 0; i < maxLen; i++) {
|
|
91
|
+
const a = linesA[i];
|
|
92
|
+
const b = linesB[i];
|
|
93
|
+
if (a === undefined && b !== undefined) {
|
|
94
|
+
diff.push(`+${i + 1}: ${b}`);
|
|
95
|
+
changes++;
|
|
96
|
+
}
|
|
97
|
+
else if (b === undefined && a !== undefined) {
|
|
98
|
+
diff.push(`-${i + 1}: ${a}`);
|
|
99
|
+
changes++;
|
|
100
|
+
}
|
|
101
|
+
else if (a !== b) {
|
|
102
|
+
diff.push(`-${i + 1}: ${a}`);
|
|
103
|
+
diff.push(`+${i + 1}: ${b}`);
|
|
104
|
+
changes++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (changes === 0)
|
|
108
|
+
return 'Files are identical.';
|
|
109
|
+
return `${changes} line(s) differ:\n${diff.join('\n')}`;
|
|
110
|
+
}
|
|
111
|
+
gitDiff(args) {
|
|
112
|
+
const target = args.path || '';
|
|
113
|
+
return this.runGit(`diff ${target}`.trim());
|
|
114
|
+
}
|
|
115
|
+
gitStaged(args) {
|
|
116
|
+
const target = args.path || '';
|
|
117
|
+
return this.runGit(`diff --staged ${target}`.trim());
|
|
118
|
+
}
|
|
119
|
+
gitCommitDiff(args) {
|
|
120
|
+
const ref = args.ref;
|
|
121
|
+
if (!ref)
|
|
122
|
+
return 'Error: ref (commit hash) is required';
|
|
123
|
+
// Sanitize ref
|
|
124
|
+
if (!/^[a-zA-Z0-9_\-./~^]+$/.test(ref))
|
|
125
|
+
return 'Error: invalid ref format';
|
|
126
|
+
return this.runGit(`show --stat --patch ${ref}`);
|
|
127
|
+
}
|
|
128
|
+
runGit(cmd) {
|
|
129
|
+
try {
|
|
130
|
+
const output = (0, child_process_1.execSync)(`git ${cmd}`, {
|
|
131
|
+
timeout: 15_000,
|
|
132
|
+
maxBuffer: 1024 * 1024,
|
|
133
|
+
encoding: 'utf-8',
|
|
134
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
135
|
+
});
|
|
136
|
+
return output.trim() || 'No changes.';
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
const e = err;
|
|
140
|
+
return `Error: ${(e.stderr || 'git command failed').trim()}`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.DiffViewerTool = DiffViewerTool;
|
|
145
|
+
//# sourceMappingURL=diff-viewer.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Tool } from '../types';
|
|
2
|
+
export declare class DockerTool implements Tool {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
permission: Tool['permission'];
|
|
6
|
+
parameters: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
action: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
args: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
cwd: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
required: string[];
|
|
23
|
+
};
|
|
24
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=docker.d.ts.map
|