feat: add CCW Loop System for automated iterative workflow execution

Implements a complete loop execution system with multi-loop parallel support,
dashboard monitoring, and comprehensive security validation.

Core features:
- Loop orchestration engine (loop-manager, loop-state-manager)
- Multi-loop parallel execution with independent state management
- REST API endpoints for loop control (pause, resume, stop, retry)
- WebSocket real-time status updates
- Dashboard Loop Monitor view with live updates
- Security: path traversal protection and sandboxed JavaScript evaluation

Test coverage:
- 42 comprehensive tests covering multi-loop, API, WebSocket, security
- Security validation for success_condition injection attacks
- Edge case handling and end-to-end workflow tests
This commit is contained in:
catlog22
2026-01-21 22:55:24 +08:00
parent 64e064e775
commit d9f1d14d5e
28 changed files with 5912 additions and 17 deletions

View File

@@ -771,9 +771,14 @@ function renderCliStatus() {
container.innerHTML = `
<div class="cli-status-header">
<h3><i data-lucide="terminal" class="w-4 h-4"></i> CLI Tools</h3>
<button class="btn-icon" onclick="refreshAllCliStatus()" title="Refresh">
<i data-lucide="refresh-cw" class="w-4 h-4"></i>
</button>
<div class="cli-status-actions">
<button class="btn-icon" onclick="syncBuiltinTools()" title="Sync tool availability with installed CLI tools">
<i data-lucide="sync" class="w-4 h-4"></i>
</button>
<button class="btn-icon" onclick="refreshAllCliStatus()" title="Refresh">
<i data-lucide="refresh-cw" class="w-4 h-4"></i>
</button>
</div>
</div>
${ccwInstallHtml}
<div class="cli-tools-grid">
@@ -825,6 +830,62 @@ function setPromptFormat(format) {
showRefreshToast(`Prompt format set to ${format.toUpperCase()}`, 'success');
}
/**
* Sync builtin tools availability with installed CLI tools
* Checks system PATH and updates cli-tools.json accordingly
*/
async function syncBuiltinTools() {
const syncButton = document.querySelector('[onclick="syncBuiltinTools()"]');
if (syncButton) {
syncButton.disabled = true;
const icon = syncButton.querySelector('i');
if (icon) icon.classList.add('spin');
}
try {
const response = await csrfFetch('/api/cli/settings/sync-tools', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
if (!response.ok) {
throw new Error('Sync failed');
}
const result = await response.json();
// Reload the config after sync
await loadCliToolsConfig();
await loadAllStatuses();
renderCliStatus();
// Show summary of changes
const { enabled, disabled, unchanged } = result.changes;
let message = 'Tools synced: ';
const parts = [];
if (enabled.length > 0) parts.push(`${enabled.join(', ')} enabled`);
if (disabled.length > 0) parts.push(`${disabled.join(', ')} disabled`);
if (unchanged.length > 0) parts.push(`${unchanged.length} unchanged`);
message += parts.join(', ');
showRefreshToast(message, 'success');
// Also invalidate the CLI tool cache to ensure fresh checks
if (window.cacheManager) {
window.cacheManager.delete('cli-tools-status');
}
} catch (err) {
console.error('Failed to sync tools:', err);
showRefreshToast('Failed to sync tools: ' + (err.message || String(err)), 'error');
} finally {
if (syncButton) {
syncButton.disabled = false;
const icon = syncButton.querySelector('i');
if (icon) icon.classList.remove('spin');
}
}
}
function setSmartContextEnabled(enabled) {
smartContextEnabled = enabled;
localStorage.setItem('ccw-smart-context', enabled.toString());