diff options
Diffstat (limited to 'makima/frontend/src/routes/contracts.tsx')
| -rw-r--r-- | makima/frontend/src/routes/contracts.tsx | 78 |
1 files changed, 72 insertions, 6 deletions
diff --git a/makima/frontend/src/routes/contracts.tsx b/makima/frontend/src/routes/contracts.tsx index aa62bd9..bb66215 100644 --- a/makima/frontend/src/routes/contracts.tsx +++ b/makima/frontend/src/routes/contracts.tsx @@ -93,6 +93,8 @@ function ContractsPageContent() { const [contractTypes, setContractTypes] = useState<ContractTypeTemplate[]>([]); const [contractTypesLoading, setContractTypesLoading] = useState(false); const [localOnly, setLocalOnly] = useState(false); + const [redTeamEnabled, setRedTeamEnabled] = useState(false); + const [redTeamPrompt, setRedTeamPrompt] = useState(""); // Fetch contract types when modal opens - merges built-in types with user templates useEffect(() => { @@ -108,11 +110,12 @@ function ContractsPageContent() { // Convert user templates to ContractTypeTemplate format, excluding built-ins return templates .filter((t: { isBuiltIn?: boolean }) => !t.isBuiltIn) - .map((t: { id: string; name: string; description: string; phases: { id: string }[] }) => ({ + .map((t: { id: string; name: string; description: string; phases: { id: string; name: string }[] }) => ({ id: t.id, name: t.name, description: t.description, phases: t.phases.map((p: { id: string }) => p.id) as ContractPhase[], + phaseNames: Object.fromEntries(t.phases.map((p: { id: string; name: string }) => [p.id, p.name])), defaultPhase: (t.phases[0]?.id || "execute") as ContractPhase, isBuiltin: false, })); @@ -265,6 +268,8 @@ function ContractsPageContent() { contractType: contractType, initialPhase: initialPhase !== defaultPhaseForType ? initialPhase : undefined, localOnly: localOnly || undefined, + redTeamEnabled: redTeamEnabled || undefined, + redTeamPrompt: redTeamEnabled && redTeamPrompt.trim() ? redTeamPrompt.trim() : undefined, }; try { @@ -340,6 +345,8 @@ function ContractsPageContent() { setRepoUrl(""); setRepoPath(""); setLocalOnly(false); + setRedTeamEnabled(false); + setRedTeamPrompt(""); setCreateError(null); }, []); @@ -652,11 +659,17 @@ function ContractsPageContent() { onChange={(e) => setInitialPhase(e.target.value as ContractPhase)} className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]" > - {(contractTypes.find((t) => t.id === contractType)?.phases || []).map((phase) => ( - <option key={phase} value={phase}> - {phase.charAt(0).toUpperCase() + phase.slice(1)} - </option> - ))} + {(() => { + const template = contractTypes.find((t) => t.id === contractType); + return (template?.phases || []).map((phase) => { + const displayName = template?.phaseNames?.[phase] || (phase.charAt(0).toUpperCase() + phase.slice(1)); + return ( + <option key={phase} value={phase}> + {displayName} + </option> + ); + }); + })()} </select> <p className="mt-1 font-mono text-xs text-[#8b949e]"> {contractType === "simple" @@ -705,6 +718,59 @@ function ContractsPageContent() { </p> </div> + {/* Red Team Monitoring */} + <div className="border-t border-[rgba(117,170,252,0.2)] pt-4"> + <div className="flex items-center gap-3"> + <button + type="button" + onClick={() => setRedTeamEnabled(!redTeamEnabled)} + className={`w-5 h-5 flex items-center justify-center border transition-colors ${ + redTeamEnabled + ? "bg-[#0f3c78] border-[#75aafc] text-[#dbe7ff]" + : "bg-[#0d1b2d] border-[#3f6fb3] text-transparent" + }`} + > + {redTeamEnabled && ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="12" + height="12" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="3" + strokeLinecap="round" + strokeLinejoin="round" + > + <polyline points="20 6 9 17 4 12" /> + </svg> + )} + </button> + <label + className="font-mono text-sm text-[#dbe7ff] cursor-pointer select-none" + onClick={() => setRedTeamEnabled(!redTeamEnabled)} + > + Enable Red Team Monitoring + </label> + </div> + <p className="font-mono text-xs text-[#8b949e] pl-8"> + Spawns a parallel task to monitor work output for quality and compliance. + </p> + {redTeamEnabled && ( + <div className="mt-3 pl-8"> + <label className="block font-mono text-xs text-[#75aafc] uppercase mb-2"> + Custom Review Criteria (Optional) + </label> + <textarea + value={redTeamPrompt} + onChange={(e) => setRedTeamPrompt(e.target.value)} + placeholder="e.g., 'Focus on security best practices' or 'Ensure all functions have tests'" + className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] text-sm font-mono h-20 resize-none focus:border-[#75aafc] focus:outline-none" + /> + </div> + )} + </div> + {/* Repository Configuration */} <div className="border-t border-[rgba(117,170,252,0.2)] pt-4"> <label className="block font-mono text-xs text-[#75aafc] uppercase mb-3"> |
