claude-gpt-collabration
Version:
MCP server for GPT-5 interactive file reading and collaboration with Claude Code
173 lines (172 loc) • 7.43 kB
JavaScript
import { callGPT5WithInput, GPT5DebugLogger, GPT5HookManager } from './gpt5.js';
import { SmartFilePreparation } from './smartFilePreparation.js';
import fs from "fs/promises";
import path from "path";
/**
* Basic file collaboration using file content injection
* This version works with any OpenAI SDK version by including file content in prompts
*/
export async function collaborateWithFilesBasic(options) {
const { root, task, model = "gpt-4-turbo", instructions, max_output_tokens = 800, upload_strategy = 'auto', manual_files, file_patterns, auto_prepare = true, max_files = 10, max_file_size = 50000 // 50KB per file limit for context
} = options;
const logger = GPT5DebugLogger.getInstance();
const functionName = 'collaborateWithFilesBasic';
const startTime = Date.now();
try {
// Execute beforeRequest hooks
await GPT5HookManager.executeHooks('beforeRequest', {
functionName,
input: options,
timestamp: new Date().toISOString()
});
// Log request
await logger.logRequest(functionName, {
root,
task: task.slice(0, 200) + (task.length > 200 ? '...[truncated]' : ''),
fullTaskLength: task.length,
model,
upload_strategy,
manual_files,
auto_prepare
});
// Step 1: Determine files to include
let filesToInclude = [];
if (upload_strategy === 'auto' && auto_prepare) {
// Smart preparation
console.log(`[${functionName}] Auto-preparing files for task...`);
const preparator = new SmartFilePreparation();
const suggestedFiles = await preparator.analyzeTaskAndPrepareFiles(task, root, max_files);
filesToInclude = suggestedFiles;
}
else if (upload_strategy === 'manual') {
// Manual preparation
if (manual_files && manual_files.length > 0) {
filesToInclude = manual_files;
}
else if (file_patterns && file_patterns.length > 0) {
// Use glob patterns to find files
for (const pattern of file_patterns) {
try {
const { glob } = await import('glob');
const files = await glob(pattern, { cwd: root, absolute: false, nodir: true });
filesToInclude.push(...files.slice(0, max_files));
}
catch (error) {
console.warn(`[${functionName}] Pattern failed: ${pattern}`, error);
}
}
}
console.log(`[${functionName}] Manually selected ${filesToInclude.length} files`);
}
// Step 2: Read file contents and build context
let fileContext = '';
let includedFiles = [];
if (filesToInclude.length > 0) {
console.log(`[${functionName}] Reading ${filesToInclude.length} files...`);
for (const relativeFile of filesToInclude) {
try {
const fullPath = path.resolve(root, relativeFile);
// Security check
if (isPathBlocked(fullPath)) {
console.warn(`[${functionName}] Blocked path: ${relativeFile}`);
continue;
}
const stats = await fs.stat(fullPath);
// Size check
if (stats.size > max_file_size) {
console.warn(`[${functionName}] File too large: ${relativeFile} (${stats.size} bytes)`);
// Include truncated version
const content = await fs.readFile(fullPath, 'utf-8');
const truncated = content.substring(0, max_file_size) + '\n... [truncated]';
fileContext += `\n\n=== File: ${relativeFile} (truncated) ===\n${truncated}\n`;
includedFiles.push({ path: relativeFile, size: max_file_size });
continue;
}
const content = await fs.readFile(fullPath, 'utf-8');
fileContext += `\n\n=== File: ${relativeFile} ===\n${content}\n`;
includedFiles.push({ path: relativeFile, size: content.length });
}
catch (error) {
console.warn(`[${functionName}] Cannot read file: ${relativeFile}`, error);
}
}
}
// Step 3: Build the prompt
let prompt = `Task: ${task}\n\n`;
if (fileContext) {
prompt += `I have provided the following project files for analysis:\n`;
prompt += includedFiles.map(f => `- ${f.path} (${f.size} chars)`).join('\n');
prompt += `\n\nFile Contents:${fileContext}\n\n`;
prompt += `Please analyze these files in the context of the task. `;
}
else {
prompt += `No specific files were provided. Please proceed with the task based on general knowledge. `;
prompt += `If you need access to specific files, please let me know which files would be helpful.\n\n`;
}
prompt += `Instructions: ${instructions || 'Provide a helpful and accurate response based on the available information.'}`;
// Step 4: Call GPT-5
console.log(`[${functionName}] Calling GPT-5 with ${fileContext.length} characters of file context`);
const response = await callGPT5WithInput({
input: prompt,
model,
max_output_tokens
});
const endTime = Date.now();
// Log response
await logger.logResponse(functionName, response, {
model,
duration: endTime - startTime,
includedFiles: includedFiles.length,
contextSize: fileContext.length
});
// Execute afterResponse hooks
await GPT5HookManager.executeHooks('afterResponse', {
functionName,
input: options,
output: response,
metadata: {
duration: endTime - startTime,
model,
includedFiles: includedFiles.length
},
timestamp: new Date().toISOString()
});
return response;
}
catch (error) {
const endTime = Date.now();
// Log error
await logger.logError(functionName, error, {
input: options,
duration: endTime - startTime
});
// Execute onError hooks
await GPT5HookManager.executeHooks('onError', {
functionName,
input: options,
error,
duration: endTime - startTime,
timestamp: new Date().toISOString()
});
return `❌ 基础文件协作过程中发生错误:${error?.message || String(error)}
任务:"${task}"
请检查配置和网络连接后重试。`;
}
}
/**
* Check if path should be blocked for security
*/
function isPathBlocked(filePath) {
const blockedPatterns = [
/\.ssh/,
/\.env/,
/\.key$/,
/\.pem$/,
/node_modules/,
/\.git/,
/password|secret|token/i,
/\.lock$/,
/\.log$/
];
return blockedPatterns.some(pattern => pattern.test(filePath));
}