Update src/ai-repo-commander.user.js
This commit is contained in:
parent
76c40fc87c
commit
945c7eca57
|
|
@ -1,40 +1,600 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name AI Repo Commander
|
// @name AI Repo Commander
|
||||||
// @namespace http://violentmonkey.com/
|
// @namespace http://tampermonkey.net/
|
||||||
// @version 1.0.0
|
// @version 1.0.0
|
||||||
// @description Enable AI assistants to interact with git repositories safely
|
// @description Enable AI assistants to securely interact with git repositories via YAML commands
|
||||||
// @author rob
|
// @author Your Name
|
||||||
// @match https://chat.openai.com/*
|
// @match https://chat.openai.com/*
|
||||||
// @match https://claude.ai/*
|
// @match https://claude.ai/*
|
||||||
// @match https://gemini.google.com/*
|
// @match https://gemini.google.com/*
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM_xmlhttpRequest
|
||||||
// @grant GM_notification
|
// @grant GM_notification
|
||||||
// @grant GM_setValue
|
// ==/UserScript==
|
||||||
// @grant GM_getValue
|
|
||||||
// ==/UserScript==
|
|
||||||
|
|
||||||
/*
|
(function() {
|
||||||
* AI Repo Commander - Main Implementation
|
'use strict';
|
||||||
* Safety-first browser extension for AI-to-repo workflows
|
|
||||||
*/
|
// Configuration - MUST be manually enabled for production
|
||||||
|
const CONFIG = {
|
||||||
(function() {
|
ENABLE_API: false, // Master kill switch
|
||||||
'use strict';
|
DEBUG_MODE: true, // Development logging
|
||||||
|
DEBOUNCE_DELAY: 5000, // 5-second bot typing protection
|
||||||
// Master configuration - SAFETY FIRST
|
MAX_RETRIES: 2, // API retry attempts
|
||||||
const CONFIG = {
|
VERSION: '1.0.0'
|
||||||
ENABLE_API: false, // MUST be manually enabled for production use
|
};
|
||||||
DEBUG_MODE: true, // Development logging
|
|
||||||
DEBOUNCE_DELAY: 5000, // 5-second bot typing protection
|
// Platform-specific DOM selectors
|
||||||
MAX_RETRIES: 2, // API retry attempts
|
const PLATFORM_SELECTORS = {
|
||||||
VERSION: '1.0.0'
|
'chat.openai.com': {
|
||||||
};
|
messages: '[class*="message"]',
|
||||||
|
input: '#prompt-textarea',
|
||||||
console.log(`🚀 AI Repo Commander v${CONFIG.VERSION} loaded!`);
|
content: '[class*="markdown"]'
|
||||||
console.log(`🔒 API Enabled: ${CONFIG.ENABLE_API}`);
|
},
|
||||||
console.log(`🐛 Debug Mode: ${CONFIG.DEBUG_MODE}`);
|
'claude.ai': {
|
||||||
|
messages: '.chat-message',
|
||||||
// TODO: Implementation will go here
|
input: '[contenteditable="true"]',
|
||||||
// This is the main file structure
|
content: '.content'
|
||||||
|
},
|
||||||
})();
|
'gemini.google.com': {
|
||||||
|
messages: '.message-content',
|
||||||
|
input: 'textarea, [contenteditable="true"]',
|
||||||
|
content: '.message-text'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Required fields matrix
|
||||||
|
const REQUIRED_FIELDS = {
|
||||||
|
'update_file': ['action', 'repo', 'path', 'content'],
|
||||||
|
'get_file': ['action', 'repo', 'path'],
|
||||||
|
'create_repo': ['action', 'repo'],
|
||||||
|
'create_file': ['action', 'repo', 'path', 'content'],
|
||||||
|
'delete_file': ['action', 'repo', 'path'],
|
||||||
|
'list_files': ['action', 'repo', 'path']
|
||||||
|
};
|
||||||
|
|
||||||
|
const FIELD_VALIDATORS = {
|
||||||
|
'repo': (value) => /^[\w\-\.]+$/.test(value),
|
||||||
|
'path': (value) => !value.includes('..') && !value.startsWith('/'),
|
||||||
|
'action': (value) => Object.keys(REQUIRED_FIELDS).includes(value),
|
||||||
|
'owner': (value) => !value || /^[\w\-]+$/.test(value),
|
||||||
|
'url': (value) => !value || /^https?:\/\/.+\..+/.test(value)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Status message templates
|
||||||
|
const STATUS_TEMPLATES = {
|
||||||
|
SUCCESS: '[{action}: Success] {details}',
|
||||||
|
ERROR: '[{action}: Error] {details}',
|
||||||
|
VALIDATION_ERROR: '[{action}: Invalid] {details}',
|
||||||
|
EXECUTING: '[{action}: Processing...]',
|
||||||
|
MOCK: '[{action}: Mock] {details}'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Command states
|
||||||
|
const COMMAND_STATES = {
|
||||||
|
DETECTED: 'detected',
|
||||||
|
PARSING: 'parsing',
|
||||||
|
VALIDATING: 'validating',
|
||||||
|
DEBOUNCING: 'debouncing',
|
||||||
|
EXECUTING: 'executing',
|
||||||
|
COMPLETE: 'complete',
|
||||||
|
ERROR: 'error'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Core Monitor Class
|
||||||
|
class CommandMonitor {
|
||||||
|
constructor() {
|
||||||
|
this.trackedMessages = new Map();
|
||||||
|
this.observer = null;
|
||||||
|
this.currentPlatform = null;
|
||||||
|
this.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.detectPlatform();
|
||||||
|
this.startObservation();
|
||||||
|
this.setupEmergencyStop();
|
||||||
|
this.log('AI Repo Commander initialized', CONFIG);
|
||||||
|
}
|
||||||
|
|
||||||
|
detectPlatform() {
|
||||||
|
const hostname = window.location.hostname;
|
||||||
|
this.currentPlatform = PLATFORM_SELECTORS[hostname] || PLATFORM_SELECTORS['chat.openai.com'];
|
||||||
|
}
|
||||||
|
|
||||||
|
startObservation() {
|
||||||
|
// Observe for new messages
|
||||||
|
this.observer = new MutationObserver((mutations) => {
|
||||||
|
mutations.forEach((mutation) => {
|
||||||
|
mutation.addedNodes.forEach((node) => {
|
||||||
|
if (node.nodeType === 1) { // Element node
|
||||||
|
this.scanNode(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start observing
|
||||||
|
const targetNode = document.body;
|
||||||
|
this.observer.observe(targetNode, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial scan of existing messages
|
||||||
|
this.scanExistingMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
scanNode(node) {
|
||||||
|
if (node.querySelector && node.querySelector(this.currentPlatform.messages)) {
|
||||||
|
this.scanMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scanExistingMessages() {
|
||||||
|
setTimeout(() => this.scanMessages(), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanMessages() {
|
||||||
|
const messages = document.querySelectorAll(this.currentPlatform.messages);
|
||||||
|
|
||||||
|
messages.forEach((messageElement) => {
|
||||||
|
const messageId = this.getMessageId(messageElement);
|
||||||
|
|
||||||
|
// Skip if already tracking this message
|
||||||
|
if (this.trackedMessages.has(messageId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = this.extractText(messageElement);
|
||||||
|
|
||||||
|
if (text && text.includes('^%$bridge')) {
|
||||||
|
this.trackMessage(messageElement, text, messageId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getMessageId(element) {
|
||||||
|
return element.id || element.className + '-' + Array.from(element.children).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractText(element) {
|
||||||
|
const contentElement = element.querySelector(this.currentPlatform.content);
|
||||||
|
return contentElement ? contentElement.textContent : element.textContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
trackMessage(element, text, messageId) {
|
||||||
|
this.log('New command detected:', { messageId, text: text.substring(0, 100) });
|
||||||
|
|
||||||
|
this.trackedMessages.set(messageId, {
|
||||||
|
element,
|
||||||
|
originalText: text,
|
||||||
|
state: COMMAND_STATES.DETECTED,
|
||||||
|
startTime: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
this.updateState(messageId, COMMAND_STATES.PARSING);
|
||||||
|
this.processCommand(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState(messageId, state) {
|
||||||
|
const message = this.trackedMessages.get(messageId);
|
||||||
|
if (message) {
|
||||||
|
message.state = state;
|
||||||
|
message.lastUpdate = Date.now();
|
||||||
|
this.trackedMessages.set(messageId, message);
|
||||||
|
this.log(`Message ${messageId} state updated to: ${state}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async processCommand(messageId) {
|
||||||
|
try {
|
||||||
|
const message = this.trackedMessages.get(messageId);
|
||||||
|
if (!message) return;
|
||||||
|
|
||||||
|
// Step 1: Parse command
|
||||||
|
const parsedCommand = CommandParser.parseYAMLCommand(message.originalText);
|
||||||
|
this.updateState(messageId, COMMAND_STATES.VALIDATING);
|
||||||
|
|
||||||
|
// Step 2: Validate command
|
||||||
|
const validation = CommandParser.validateStructure(parsedCommand);
|
||||||
|
if (!validation.isValid) {
|
||||||
|
throw new Error(`Validation failed: ${validation.errors.join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Debounce (wait for bot to finish typing)
|
||||||
|
this.updateState(messageId, COMMAND_STATES.DEBOUNCING);
|
||||||
|
await this.debounce();
|
||||||
|
|
||||||
|
// Step 4: Execute command
|
||||||
|
this.updateState(messageId, COMMAND_STATES.EXECUTING);
|
||||||
|
const result = await ExecutionManager.executeCommand(parsedCommand, message.element);
|
||||||
|
|
||||||
|
// Step 5: Complete
|
||||||
|
this.updateState(messageId, COMMAND_STATES.COMPLETE);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.log(`Command processing error: ${error.message}`);
|
||||||
|
this.updateState(messageId, COMMAND_STATES.ERROR);
|
||||||
|
|
||||||
|
const message = this.trackedMessages.get(messageId);
|
||||||
|
if (message) {
|
||||||
|
UIFeedback.replaceWithStatus(message.element, 'ERROR', {
|
||||||
|
action: 'Command',
|
||||||
|
details: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debounce() {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, CONFIG.DEBOUNCE_DELAY));
|
||||||
|
}
|
||||||
|
|
||||||
|
stopAllProcessing() {
|
||||||
|
this.trackedMessages.clear();
|
||||||
|
if (this.observer) {
|
||||||
|
this.observer.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEmergencyStop() {
|
||||||
|
window.AI_REPO_STOP = () => {
|
||||||
|
CONFIG.ENABLE_API = false;
|
||||||
|
this.stopAllProcessing();
|
||||||
|
this.log('EMERGENCY STOP ACTIVATED');
|
||||||
|
GM_notification({
|
||||||
|
text: 'AI Repo Commander Emergency Stop Activated',
|
||||||
|
title: 'Safety System',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
log(...args) {
|
||||||
|
if (CONFIG.DEBUG_MODE) {
|
||||||
|
console.log('[AI Repo Commander]', ...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command Parser Class
|
||||||
|
class CommandParser {
|
||||||
|
static parseYAMLCommand(text) {
|
||||||
|
try {
|
||||||
|
// Extract command block between ^%$bridge and ---
|
||||||
|
const commandBlock = this.extractCommandBlock(text);
|
||||||
|
if (!commandBlock) {
|
||||||
|
throw new Error('No valid command block found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse YAML-like syntax
|
||||||
|
const parsed = this.parseKeyValuePairs(commandBlock);
|
||||||
|
|
||||||
|
// Set defaults
|
||||||
|
parsed.url = parsed.url || 'https://n8n.brrd.tech/webhook/ai-gitea-bridge';
|
||||||
|
parsed.owner = parsed.owner || 'brrd';
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`YAML parsing failed: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static extractCommandBlock(text) {
|
||||||
|
const startMarker = '^%$bridge';
|
||||||
|
const endMarker = '---';
|
||||||
|
|
||||||
|
const startIndex = text.indexOf(startMarker);
|
||||||
|
if (startIndex === -1) return null;
|
||||||
|
|
||||||
|
const endIndex = text.indexOf(endMarker, startIndex + startMarker.length);
|
||||||
|
if (endIndex === -1) return null;
|
||||||
|
|
||||||
|
return text.substring(startIndex + startMarker.length, endIndex).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
static parseKeyValuePairs(block) {
|
||||||
|
const lines = block.split('\n');
|
||||||
|
const result = {};
|
||||||
|
let currentKey = null;
|
||||||
|
let multiLineContent = null;
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
|
||||||
|
if (!trimmed) continue;
|
||||||
|
|
||||||
|
// Check for multi-line content marker
|
||||||
|
if (trimmed === '|' && currentKey === 'content') {
|
||||||
|
multiLineContent = [];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're collecting multi-line content
|
||||||
|
if (multiLineContent !== null) {
|
||||||
|
if (trimmed === '---') break; // End of command
|
||||||
|
multiLineContent.push(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse key: value pairs
|
||||||
|
const colonIndex = trimmed.indexOf(':');
|
||||||
|
if (colonIndex !== -1) {
|
||||||
|
const key = trimmed.substring(0, colonIndex).trim();
|
||||||
|
let value = trimmed.substring(colonIndex + 1).trim();
|
||||||
|
|
||||||
|
// Handle empty values that might be multi-line
|
||||||
|
if (value === '' || value === '|') {
|
||||||
|
currentKey = key;
|
||||||
|
if (value === '|') {
|
||||||
|
multiLineContent = [];
|
||||||
|
}
|
||||||
|
result[key] = '';
|
||||||
|
} else {
|
||||||
|
result[key] = value;
|
||||||
|
currentKey = null;
|
||||||
|
}
|
||||||
|
} else if (currentKey && result[currentKey] === '') {
|
||||||
|
// Continue with previous key (simple multi-line)
|
||||||
|
result[currentKey] += '\n' + trimmed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join multi-line content
|
||||||
|
if (multiLineContent !== null && currentKey) {
|
||||||
|
result[currentKey] = multiLineContent.join('\n').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static validateStructure(parsed) {
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
// Check required fields
|
||||||
|
const action = parsed.action;
|
||||||
|
if (!action) {
|
||||||
|
errors.push('Missing required field: action');
|
||||||
|
return { isValid: false, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
const requiredFields = REQUIRED_FIELDS[action];
|
||||||
|
if (!requiredFields) {
|
||||||
|
errors.push(`Unknown action: ${action}`);
|
||||||
|
return { isValid: false, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const field of requiredFields) {
|
||||||
|
if (!parsed[field] && parsed[field] !== '') {
|
||||||
|
errors.push(`Missing required field: ${field}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate field formats
|
||||||
|
for (const [field, value] of Object.entries(parsed)) {
|
||||||
|
const validator = FIELD_VALIDATORS[field];
|
||||||
|
if (validator && !validator(value)) {
|
||||||
|
errors.push(`Invalid format for field: ${field}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isValid: errors.length === 0,
|
||||||
|
errors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execution Manager Class
|
||||||
|
class ExecutionManager {
|
||||||
|
static async executeCommand(command, sourceElement) {
|
||||||
|
try {
|
||||||
|
// Pre-execution safety checks
|
||||||
|
if (!CONFIG.ENABLE_API) {
|
||||||
|
return this.mockExecution(command, sourceElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show executing status
|
||||||
|
UIFeedback.replaceWithStatus(sourceElement, 'EXECUTING', {
|
||||||
|
action: command.action,
|
||||||
|
details: 'Making API request...'
|
||||||
|
});
|
||||||
|
|
||||||
|
// API call with retry logic
|
||||||
|
const response = await this.makeAPICallWithRetry(command);
|
||||||
|
return this.handleSuccess(response, command, sourceElement);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return this.handleError(error, command, sourceElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async makeAPICallWithRetry(command, attempt = 0) {
|
||||||
|
try {
|
||||||
|
return await this.makeAPICall(command);
|
||||||
|
} catch (error) {
|
||||||
|
if (attempt < CONFIG.MAX_RETRIES) {
|
||||||
|
await this.delay(1000 * (attempt + 1));
|
||||||
|
return this.makeAPICallWithRetry(command, attempt + 1);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static makeAPICall(command) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
GM_xmlhttpRequest({
|
||||||
|
method: 'POST',
|
||||||
|
url: command.url,
|
||||||
|
headers: {
|
||||||
|
'X-Bridge-Key': 'mango-rocket-82',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
data: JSON.stringify(command),
|
||||||
|
timeout: 30000,
|
||||||
|
onload: (response) => {
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
resolve(response);
|
||||||
|
} else {
|
||||||
|
reject(new Error(`API Error ${response.status}: ${response.statusText}`));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onerror: (error) => {
|
||||||
|
reject(new Error(`Network error: ${error}`));
|
||||||
|
},
|
||||||
|
ontimeout: () => {
|
||||||
|
reject(new Error('API request timeout'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async mockExecution(command, sourceElement) {
|
||||||
|
await this.delay(1000); // Simulate API delay
|
||||||
|
|
||||||
|
const mockResponse = {
|
||||||
|
status: 200,
|
||||||
|
responseText: JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
message: `Mock execution completed for ${command.action}`,
|
||||||
|
data: { command: command.action, repo: command.repo }
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.handleSuccess(mockResponse, command, sourceElement, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static handleSuccess(response, command, sourceElement, isMock = false) {
|
||||||
|
const responseData = JSON.parse(response.responseText);
|
||||||
|
const templateType = isMock ? 'MOCK' : 'SUCCESS';
|
||||||
|
|
||||||
|
UIFeedback.replaceWithStatus(sourceElement, templateType, {
|
||||||
|
action: command.action,
|
||||||
|
details: responseData.message || 'Operation completed successfully'
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: responseData,
|
||||||
|
isMock
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static handleError(error, command, sourceElement) {
|
||||||
|
UIFeedback.replaceWithStatus(sourceElement, 'ERROR', {
|
||||||
|
action: command.action,
|
||||||
|
details: error.message
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static delay(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI Feedback System
|
||||||
|
class UIFeedback {
|
||||||
|
static replaceWithStatus(sourceElement, templateType, data) {
|
||||||
|
const statusElement = this.createStatusElement(templateType, data);
|
||||||
|
this.replaceElement(sourceElement, statusElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createStatusElement(templateType, data) {
|
||||||
|
const template = STATUS_TEMPLATES[templateType];
|
||||||
|
const message = template
|
||||||
|
.replace('{action}', data.action)
|
||||||
|
.replace('{details}', data.details);
|
||||||
|
|
||||||
|
const statusElement = document.createElement('div');
|
||||||
|
statusElement.className = 'ai-repo-commander-status';
|
||||||
|
statusElement.textContent = message;
|
||||||
|
statusElement.style.cssText = `
|
||||||
|
padding: 8px 12px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-left: 4px solid ${this.colorCodeStatus(templateType)};
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
`;
|
||||||
|
|
||||||
|
return statusElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
static replaceElement(oldElement, newElement) {
|
||||||
|
if (oldElement && oldElement.parentNode) {
|
||||||
|
oldElement.parentNode.replaceChild(newElement, oldElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static colorCodeStatus(type) {
|
||||||
|
const colors = {
|
||||||
|
'SUCCESS': '#10B981', // Green
|
||||||
|
'ERROR': '#EF4444', // Red
|
||||||
|
'VALIDATION_ERROR': '#F59E0B', // Yellow
|
||||||
|
'EXECUTING': '#3B82F6', // Blue
|
||||||
|
'MOCK': '#8B5CF6' // Purple
|
||||||
|
};
|
||||||
|
return colors[type] || '#6B7280'; // Default gray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test command generator
|
||||||
|
const TEST_COMMANDS = {
|
||||||
|
validUpdate: `^%$bridge
|
||||||
|
action: update_file
|
||||||
|
repo: test-repo
|
||||||
|
path: TEST.md
|
||||||
|
content: |
|
||||||
|
Test content
|
||||||
|
Multiple lines
|
||||||
|
---`,
|
||||||
|
|
||||||
|
invalidCommand: `^%$bridge
|
||||||
|
action: update_file
|
||||||
|
repo: test-repo
|
||||||
|
---`,
|
||||||
|
|
||||||
|
getFile: `^%$bridge
|
||||||
|
action: get_file
|
||||||
|
repo: test-repo
|
||||||
|
path: README.md
|
||||||
|
---`
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize the system
|
||||||
|
let commandMonitor;
|
||||||
|
|
||||||
|
function initializeRepoCommander() {
|
||||||
|
if (!commandMonitor) {
|
||||||
|
commandMonitor = new CommandMonitor();
|
||||||
|
|
||||||
|
// Expose for debugging
|
||||||
|
window.AI_REPO_COMMANDER = {
|
||||||
|
monitor: commandMonitor,
|
||||||
|
config: CONFIG,
|
||||||
|
test: TEST_COMMANDS,
|
||||||
|
version: CONFIG.VERSION
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('AI Repo Commander fully initialized');
|
||||||
|
console.log('API Enabled:', CONFIG.ENABLE_API);
|
||||||
|
console.log('Test commands available in window.AI_REPO_COMMANDER.test');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for DOM to be ready
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initializeRepoCommander);
|
||||||
|
} else {
|
||||||
|
initializeRepoCommander();
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
Loading…
Reference in New Issue