Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .opencode/agents/enforcer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: enforcer
description: "Enforcer agent for codex compliance and error prevention"
version: "1.0.0"

system: "You are the StringRay Enforcer agent. You validate code against the Universal Development Codex (60 error-prevention terms), enforce quality gates, and prevent errors with 99.6% accuracy. You check for type safety, input validation, memory optimization, error handling, security, and architecture compliance. Block operations that violate codex terms."

# =============================================================================
# CODEX COMPLIANCE
# =============================================================================
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/unit/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ vi.mock("child_process", () => ({
})),
}));

vi.mock("../mcps/agent-resolver.js", () => ({
vi.mock("../../mcps/agent-resolver.js", () => ({
resolveAgent: vi.fn().mockResolvedValue({
name: "enforcer",
system: "You are a code quality enforcer...",
Expand Down
25 changes: 17 additions & 8 deletions src/mcps/orchestrator/execution/execution-planner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export class ExecutionPlanner {
errors.push(`Task ${i}: Missing required field 'type'`);
}

// Check dependencies exist
if (task.dependencies && task.id) {
// Check dependencies exist (only for string[] dependency IDs)
if (task.dependencies && task.id && Array.isArray(task.dependencies)) {
const taskIds = new Set(tasks.map(t => t.id).filter((id): id is string => !!id));
for (const depId of task.dependencies) {
if (!taskIds.has(depId)) {
Expand Down Expand Up @@ -145,7 +145,10 @@ export class ExecutionPlanner {
const dependentTasks: OrchestrationTask[] = [];

for (const task of sortedTasks) {
if (!task.dependencies || task.dependencies.length === 0) {
const hasDeps = task.dependencies
? (Array.isArray(task.dependencies) ? task.dependencies.length : Number(task.dependencies)) > 0
: false;
if (!hasDeps) {
independentTasks.push(task);
} else {
dependentTasks.push(task);
Expand All @@ -171,7 +174,7 @@ export class ExecutionPlanner {
if (visited.has(task.id)) return;
visited.add(task.id);

if (task.dependencies) {
if (task.dependencies && Array.isArray(task.dependencies)) {
for (const depId of task.dependencies) {
const depTask = taskMap.get(depId);
if (depTask) visit(depTask);
Expand Down Expand Up @@ -314,9 +317,12 @@ export class ExecutionPlanner {
complexity = (complexity + task.estimatedComplexity) / 2;
}

// Adjust based on dependencies
// Adjust based on dependencies (handles both string[] and number)
if (task.dependencies) {
complexity += task.dependencies.length * 5;
const depCount = Array.isArray(task.dependencies)
? task.dependencies.length
: Number(task.dependencies) || 0;
complexity += depCount * 5;
}

return Math.min(Math.max(Math.round(complexity), 1), 100);
Expand All @@ -337,8 +343,11 @@ export class ExecutionPlanner {
*/
private calculateParallelPotential(tasks: OrchestrationTask[]): number {
// Tasks without dependencies can run in parallel
const independent = tasks.filter(t => !t.dependencies || t.dependencies.length === 0);
return independent.length / tasks.length;
const independent = tasks.filter(
t => !t.dependencies ||
(Array.isArray(t.dependencies) ? t.dependencies.length : Number(t.dependencies)) === 0
);
return tasks.length > 0 ? independent.length / tasks.length : 0;
}
}

Expand Down
20 changes: 11 additions & 9 deletions src/mcps/orchestrator/handlers/status-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,34 +258,36 @@ ${optimizations.map((o) => `• ${o}`).join('\n')}
private formatStatusResponse(status: unknown, detailed: boolean): string {
const statusAny = status as Record<string, unknown>;

// Check if it's a completed session
if (statusAny.completed) {
// Check if it's a completed session (supports both 'completed' boolean and status: 'completed')
if (statusAny.completed || statusAny.status === 'completed') {
const result = statusAny as { success: boolean; duration: number; completedTasks: number; failedTasks: number };
return `📊 Session Status: COMPLETED

**Success:** ${result.success ? '✅ Yes' : '❌ No'}
**Duration:** ${result.duration}ms
**Tasks:** ${result.completedTasks + result.failedTasks} (${result.completedTasks} ✅, ${result.failedTasks} ❌)`;
**Tasks:** ${(result.completedTasks ?? 0) + (result.failedTasks ?? 0)} (${result.completedTasks ?? 0} ✅, ${result.failedTasks ?? 0} ❌)`;
}

// Overall status
const orchStatus = status as OrchestrationStatus;

let response = `📊 Orchestration Status

**Active Sessions:** ${orchStatus.activeSessions}
**Total Tasks Executed:** ${orchStatus.totalTasks}`;
**Active Sessions:** ${orchStatus.activeSessions ?? 0}
**Total Tasks Executed:** ${orchStatus.totalTasks ?? 0}`;

if (Object.keys(orchStatus.agentUtilization).length > 0) {
const agentUtil = orchStatus.agentUtilization || {};
if (Object.keys(agentUtil).length > 0) {
response += `\n\n**Agent Utilization:**`;
for (const [agent, count] of Object.entries(orchStatus.agentUtilization)) {
for (const [agent, count] of Object.entries(agentUtil)) {
response += `\n• ${agent}: ${count} tasks`;
}
}

if (detailed && orchStatus.recentSessions.length > 0) {
const sessions = orchStatus.recentSessions || [];
if (detailed && sessions.length > 0) {
response += `\n\n**Recent Sessions:**`;
for (const session of orchStatus.recentSessions) {
for (const session of sessions) {
response += `\n• ${session.sessionId}: ${session.tasks} tasks, ${session.duration}ms`;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/mcps/orchestrator/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface OrchestrationTask {
description: string;
type: string;
priority?: 'critical' | 'high' | 'medium' | 'low';
dependencies?: string[];
dependencies?: string[] | number;
estimatedComplexity?: number;
}

Expand Down
3 changes: 2 additions & 1 deletion src/mcps/security-scan.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,11 @@ class StrRaySecurityScanServer {
try {
// 1. Dependency Vulnerability Scanning
if (scope === "dependencies" || scope === "full") {
const detectedPm = this.detectPackageManager("auto");
const depResults = await this.scanDependencies(
auditLevel,
includeOutdated,
"npm",
detectedPm,
);
results.vulnerabilities.push(...depResults.vulnerabilities);
results.recommendations.push(...depResults.recommendations);
Expand Down