2026-01-27 12:11:00 -08:00
|
|
|
// Completion markers for plan2code-loop
|
2026-02-17 09:13:23 -08:00
|
|
|
const COMPLETION_MARKERS = ['TASK_COMPLETE', 'TASK_BLOCKED', 'LOOP_COMPLETE', 'PREREQ_COMPLETE', 'PREREQ_ASSUMED'] as const;
|
2026-01-27 12:11:00 -08:00
|
|
|
|
|
|
|
|
export type CompletionMarker = (typeof COMPLETION_MARKERS)[number];
|
|
|
|
|
|
|
|
|
|
export interface CompletionCheckResult {
|
|
|
|
|
completed: boolean;
|
|
|
|
|
marker?: CompletionMarker;
|
|
|
|
|
taskId?: string; // e.g., "1.1", "2.3"
|
|
|
|
|
taskName?: string; // e.g., "Initialize project structure"
|
|
|
|
|
reason?: string; // For TASK_BLOCKED: reason
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function checkForCompletion(output: string): CompletionCheckResult {
|
|
|
|
|
// Check for LOOP_COMPLETE first (highest priority)
|
|
|
|
|
if (output.includes('LOOP_COMPLETE')) {
|
|
|
|
|
return { completed: true, marker: 'LOOP_COMPLETE' };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for TASK_COMPLETE with task info
|
|
|
|
|
// Format: TASK_COMPLETE: 1.1 - Task description
|
|
|
|
|
// Or: TASK_COMPLETE: 1.1: Task description
|
|
|
|
|
// Or: TASK_COMPLETE[1.1]: Task description
|
|
|
|
|
const completeMatch = output.match(/TASK_COMPLETE[:\[\s]+(\d+\.\d+)[\]:\-\s]+(.+?)(?:\n|$)/i);
|
|
|
|
|
if (completeMatch) {
|
|
|
|
|
return {
|
|
|
|
|
completed: true,
|
|
|
|
|
marker: 'TASK_COMPLETE',
|
|
|
|
|
taskId: completeMatch[1],
|
|
|
|
|
taskName: completeMatch[2].trim(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Simple TASK_COMPLETE without structured info - try to extract task from context
|
|
|
|
|
if (output.includes('TASK_COMPLETE')) {
|
|
|
|
|
// Try to find task info nearby
|
|
|
|
|
const taskMatch = output.match(/(?:task|completed?)\s+(\d+\.\d+)[:\s]+([^\n]{5,80})/i);
|
|
|
|
|
return {
|
|
|
|
|
completed: true,
|
|
|
|
|
marker: 'TASK_COMPLETE',
|
|
|
|
|
taskId: taskMatch?.[1],
|
|
|
|
|
taskName: taskMatch?.[2]?.trim(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-17 09:13:23 -08:00
|
|
|
// Check for PREREQ_COMPLETE with prereq info
|
|
|
|
|
// Format: PREREQ_COMPLETE: P1.1 - Verified Phase 1 complete
|
|
|
|
|
const prereqMatch = output.match(/PREREQ_COMPLETE[:\[\s]+([^\]:\-\s]+)[\]:\-\s]+(.+?)(?:\n|$)/i);
|
|
|
|
|
if (prereqMatch) {
|
|
|
|
|
return {
|
|
|
|
|
completed: true,
|
|
|
|
|
marker: 'PREREQ_COMPLETE',
|
|
|
|
|
taskId: prereqMatch[1],
|
|
|
|
|
taskName: prereqMatch[2].trim(),
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-02-18 15:16:11 -08:00
|
|
|
|
2026-02-17 09:13:23 -08:00
|
|
|
// Check for PREREQ_ASSUMED with prereq info
|
|
|
|
|
// Format: PREREQ_ASSUMED: P2.1 - Design approval (cannot verify)
|
|
|
|
|
const assumedMatch = output.match(/PREREQ_ASSUMED[:\[\s]+([^\]:\-\s]+)[\]:\-\s]+(.+?)(?:\n|$)/i);
|
|
|
|
|
if (assumedMatch) {
|
|
|
|
|
return {
|
|
|
|
|
completed: true,
|
|
|
|
|
marker: 'PREREQ_ASSUMED',
|
|
|
|
|
taskId: assumedMatch[1],
|
|
|
|
|
taskName: assumedMatch[2].trim(),
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-02-18 15:16:11 -08:00
|
|
|
|
2026-01-27 12:11:00 -08:00
|
|
|
// Check for TASK_BLOCKED with task info and reason
|
|
|
|
|
// Format: TASK_BLOCKED: 1.1 - Reason why blocked
|
|
|
|
|
const blockedWithTaskMatch = output.match(/TASK_BLOCKED[:\[\s]+(\d+\.\d+)[\]:\-\s]+(.+?)(?:\n|$)/i);
|
|
|
|
|
if (blockedWithTaskMatch) {
|
|
|
|
|
return {
|
|
|
|
|
completed: true,
|
|
|
|
|
marker: 'TASK_BLOCKED',
|
|
|
|
|
taskId: blockedWithTaskMatch[1],
|
|
|
|
|
reason: blockedWithTaskMatch[2].trim(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TASK_BLOCKED with just reason (no task ID)
|
|
|
|
|
const blockedMatch = output.match(/TASK_BLOCKED:\s*(.+?)(?:\n|$)/);
|
|
|
|
|
if (blockedMatch) {
|
|
|
|
|
return {
|
|
|
|
|
completed: true,
|
|
|
|
|
marker: 'TASK_BLOCKED',
|
|
|
|
|
reason: blockedMatch[1].trim()
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Simple TASK_BLOCKED without any info
|
|
|
|
|
if (output.includes('TASK_BLOCKED')) {
|
|
|
|
|
return { completed: true, marker: 'TASK_BLOCKED', reason: 'No reason provided' };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { completed: false };
|
|
|
|
|
}
|
2026-02-18 15:16:11 -08:00
|
|
|
|
2026-02-17 09:13:23 -08:00
|
|
|
/**
|
|
|
|
|
* Extract ALL completion markers from output (for phase mode).
|
|
|
|
|
* Returns an array of all TASK_COMPLETE/TASK_BLOCKED markers found,
|
|
|
|
|
* plus whether LOOP_COMPLETE or PHASE_COMPLETE was present.
|
|
|
|
|
*/
|
|
|
|
|
export function checkForAllCompletions(output: string): {
|
|
|
|
|
tasks: CompletionCheckResult[];
|
|
|
|
|
loopComplete: boolean;
|
|
|
|
|
phaseComplete: boolean;
|
|
|
|
|
} {
|
|
|
|
|
const tasks: CompletionCheckResult[] = [];
|
|
|
|
|
let loopComplete = false;
|
|
|
|
|
let phaseComplete = false;
|
2026-02-18 15:16:11 -08:00
|
|
|
|
2026-02-17 09:13:23 -08:00
|
|
|
if (output.includes('LOOP_COMPLETE')) {
|
|
|
|
|
loopComplete = true;
|
|
|
|
|
}
|
2026-02-18 15:16:11 -08:00
|
|
|
|
2026-02-17 09:13:23 -08:00
|
|
|
if (output.includes('PHASE_COMPLETE')) {
|
|
|
|
|
phaseComplete = true;
|
|
|
|
|
}
|
2026-02-18 15:16:11 -08:00
|
|
|
|
2026-02-17 09:13:23 -08:00
|
|
|
// Find all TASK_COMPLETE markers with task info
|
|
|
|
|
// Format: TASK_COMPLETE: 1.1 - Task description
|
|
|
|
|
const completeRegex = /TASK_COMPLETE[:\[\s]+(\d+\.\d+)[\]:\-\s]+(.+?)(?:\n|$)/gi;
|
|
|
|
|
let match: RegExpExecArray | null;
|
|
|
|
|
while ((match = completeRegex.exec(output)) !== null) {
|
|
|
|
|
tasks.push({
|
|
|
|
|
completed: true,
|
|
|
|
|
marker: 'TASK_COMPLETE',
|
|
|
|
|
taskId: match[1],
|
|
|
|
|
taskName: match[2].trim(),
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-02-18 15:16:11 -08:00
|
|
|
|
2026-02-17 09:13:23 -08:00
|
|
|
// Find all TASK_BLOCKED markers with task info
|
|
|
|
|
const blockedRegex = /TASK_BLOCKED[:\[\s]+(\d+\.\d+)[\]:\-\s]+(.+?)(?:\n|$)/gi;
|
|
|
|
|
while ((match = blockedRegex.exec(output)) !== null) {
|
|
|
|
|
tasks.push({
|
|
|
|
|
completed: true,
|
|
|
|
|
marker: 'TASK_BLOCKED',
|
|
|
|
|
taskId: match[1],
|
|
|
|
|
reason: match[2].trim(),
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-02-18 15:16:11 -08:00
|
|
|
|
2026-02-17 09:13:23 -08:00
|
|
|
// Find PREREQ_COMPLETE markers
|
|
|
|
|
const prereqRegex = /PREREQ_COMPLETE[:\[\s]+([^\]:\-\s]+)[\]:\-\s]+(.+?)(?:\n|$)/gi;
|
|
|
|
|
while ((match = prereqRegex.exec(output)) !== null) {
|
|
|
|
|
tasks.push({
|
|
|
|
|
completed: true,
|
|
|
|
|
marker: 'PREREQ_COMPLETE',
|
|
|
|
|
taskId: match[1],
|
|
|
|
|
taskName: match[2].trim(),
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-02-18 15:16:11 -08:00
|
|
|
|
2026-02-17 09:13:23 -08:00
|
|
|
// Find PREREQ_ASSUMED markers
|
|
|
|
|
const assumedRegex = /PREREQ_ASSUMED[:\[\s]+([^\]:\-\s]+)[\]:\-\s]+(.+?)(?:\n|$)/gi;
|
|
|
|
|
while ((match = assumedRegex.exec(output)) !== null) {
|
|
|
|
|
tasks.push({
|
|
|
|
|
completed: true,
|
|
|
|
|
marker: 'PREREQ_ASSUMED',
|
|
|
|
|
taskId: match[1],
|
|
|
|
|
taskName: match[2].trim(),
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-02-18 15:16:11 -08:00
|
|
|
|
2026-02-17 09:13:23 -08:00
|
|
|
return { tasks, loopComplete, phaseComplete };
|
|
|
|
|
}
|