104 lines
3.1 KiB
JavaScript
104 lines
3.1 KiB
JavaScript
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
function resolvePath(workspace, p) {
|
|
if (path.isAbsolute(p)) return p;
|
|
return path.join(workspace, p);
|
|
}
|
|
|
|
function readLines(content) {
|
|
return content.split(/\r?\n/);
|
|
}
|
|
|
|
function applyMaxChars(text, maxChars) {
|
|
if (!maxChars || text.length <= maxChars) return { text, truncated: false };
|
|
return { text: text.slice(0, maxChars), truncated: true };
|
|
}
|
|
|
|
function readFileTool(workspace, args) {
|
|
const target = resolvePath(workspace, args.path);
|
|
const type = args.type || 'read';
|
|
try {
|
|
const raw = fs.readFileSync(target, 'utf8');
|
|
if (type === 'read') {
|
|
const lines = readLines(raw);
|
|
const start = Math.max(1, args.start_line || 1);
|
|
const end = Math.min(lines.length, args.end_line || lines.length);
|
|
const slice = lines.slice(start - 1, end).join('\n');
|
|
const capped = applyMaxChars(slice, args.max_chars);
|
|
return {
|
|
success: true,
|
|
type: 'read',
|
|
path: target,
|
|
line_start: start,
|
|
line_end: end,
|
|
content: capped.text,
|
|
truncated: capped.truncated,
|
|
};
|
|
}
|
|
if (type === 'search') {
|
|
const lines = readLines(raw);
|
|
const query = args.query || '';
|
|
const caseSensitive = !!args.case_sensitive;
|
|
const maxMatches = args.max_matches || 20;
|
|
const before = args.context_before || 0;
|
|
const after = args.context_after || 0;
|
|
const matches = [];
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i];
|
|
const hay = caseSensitive ? line : line.toLowerCase();
|
|
const needle = caseSensitive ? query : query.toLowerCase();
|
|
if (!needle) break;
|
|
if (hay.includes(needle)) {
|
|
const start = Math.max(0, i - before);
|
|
const end = Math.min(lines.length - 1, i + after);
|
|
const snippet = lines.slice(start, end + 1).join('\n');
|
|
matches.push({
|
|
id: `match_${matches.length + 1}`,
|
|
line_start: start + 1,
|
|
line_end: end + 1,
|
|
hits: [i + 1],
|
|
snippet,
|
|
});
|
|
if (matches.length >= maxMatches) break;
|
|
}
|
|
}
|
|
return {
|
|
success: true,
|
|
type: 'search',
|
|
path: target,
|
|
query,
|
|
case_sensitive: caseSensitive,
|
|
matches,
|
|
};
|
|
}
|
|
if (type === 'extract') {
|
|
const lines = readLines(raw);
|
|
const segments = Array.isArray(args.segments) ? args.segments : [];
|
|
const extracted = segments.map((seg, idx) => {
|
|
const start = Math.max(1, seg.start_line || 1);
|
|
const end = Math.min(lines.length, seg.end_line || lines.length);
|
|
return {
|
|
label: seg.label || `segment_${idx + 1}`,
|
|
line_start: start,
|
|
line_end: end,
|
|
content: lines.slice(start - 1, end).join('\n'),
|
|
};
|
|
});
|
|
return {
|
|
success: true,
|
|
type: 'extract',
|
|
path: target,
|
|
segments: extracted,
|
|
};
|
|
}
|
|
return { success: false, error: '未知 read_file type' };
|
|
} catch (err) {
|
|
return { success: false, error: err.message || String(err) };
|
|
}
|
|
}
|
|
|
|
module.exports = { readFileTool, resolvePath };
|