summaryrefslogtreecommitdiff
path: root/makima/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend')
-rw-r--r--makima/frontend/src/components/NavStrip.tsx1
-rw-r--r--makima/frontend/src/components/directives/ApprovalsTab.tsx77
-rw-r--r--makima/frontend/src/components/directives/ChainTab.tsx212
-rw-r--r--makima/frontend/src/components/directives/CreateDirectiveModal.tsx146
-rw-r--r--makima/frontend/src/components/directives/DirectiveDetail.tsx160
-rw-r--r--makima/frontend/src/components/directives/DirectiveList.tsx85
-rw-r--r--makima/frontend/src/components/directives/DirectiveListItem.tsx83
-rw-r--r--makima/frontend/src/components/directives/EvaluationsTab.tsx12
-rw-r--r--makima/frontend/src/components/directives/EventsTab.tsx77
-rw-r--r--makima/frontend/src/components/directives/OverviewTab.tsx73
-rw-r--r--makima/frontend/src/components/directives/StepNode.tsx87
-rw-r--r--makima/frontend/src/components/directives/VerifiersTab.tsx12
-rw-r--r--makima/frontend/src/components/directives/index.ts11
-rw-r--r--makima/frontend/src/hooks/useDirectiveDetail.ts125
-rw-r--r--makima/frontend/src/hooks/useDirectives.ts298
-rw-r--r--makima/frontend/src/lib/api.ts1283
-rw-r--r--makima/frontend/src/main.tsx17
-rw-r--r--makima/frontend/src/routes/directives.tsx184
-rw-r--r--makima/frontend/tsconfig.tsbuildinfo2
19 files changed, 1 insertions, 2944 deletions
diff --git a/makima/frontend/src/components/NavStrip.tsx b/makima/frontend/src/components/NavStrip.tsx
index 9bb7777..fb95c7f 100644
--- a/makima/frontend/src/components/NavStrip.tsx
+++ b/makima/frontend/src/components/NavStrip.tsx
@@ -10,7 +10,6 @@ interface NavLink {
const NAV_LINKS: NavLink[] = [
{ label: "Listen", href: "/listen" },
- { label: "Directives", href: "/directives", requiresAuth: true },
{ label: "Contracts", href: "/contracts", requiresAuth: true },
{ label: "Board", href: "/workflow", requiresAuth: true },
{ label: "Mesh", href: "/mesh", requiresAuth: true },
diff --git a/makima/frontend/src/components/directives/ApprovalsTab.tsx b/makima/frontend/src/components/directives/ApprovalsTab.tsx
deleted file mode 100644
index dca48df..0000000
--- a/makima/frontend/src/components/directives/ApprovalsTab.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import type { DirectiveWithProgress } from "../../lib/api";
-
-export function ApprovalsTab({ directive, onRefresh }: { directive: DirectiveWithProgress; onRefresh: () => void }) {
- if (directive.pendingApprovals.length === 0) {
- return (
- <div className="text-center py-8">
- <p className="font-mono text-sm text-[#556677]">No pending approvals</p>
- </div>
- );
- }
-
- const handleApprove = async (approvalId: string) => {
- try {
- const { approveDirectiveRequest } = await import("../../lib/api");
- await approveDirectiveRequest(directive.id, approvalId);
- onRefresh();
- } catch (err) {
- console.error("Failed to approve:", err);
- }
- };
-
- const handleDeny = async (approvalId: string) => {
- try {
- const { denyDirectiveRequest } = await import("../../lib/api");
- await denyDirectiveRequest(directive.id, approvalId);
- onRefresh();
- } catch (err) {
- console.error("Failed to deny:", err);
- }
- };
-
- return (
- <div className="space-y-3">
- {directive.pendingApprovals.map((approval) => {
- const urgencyColor = {
- low: "text-[#556677]",
- normal: "text-[#75aafc]",
- high: "text-yellow-400",
- critical: "text-red-400",
- }[approval.urgency] || "text-[#556677]";
-
- return (
- <div
- key={approval.id}
- className="p-4 bg-[rgba(117,170,252,0.05)] border border-[rgba(117,170,252,0.2)]"
- >
- <div className="flex items-start justify-between">
- <div>
- <div className="flex items-center gap-2">
- <span className="font-mono text-sm text-[#dbe7ff]">{approval.approvalType}</span>
- <span className={`font-mono text-[10px] uppercase ${urgencyColor}`}>
- {approval.urgency}
- </span>
- </div>
- <p className="font-mono text-xs text-[#9bc3ff] mt-1">{approval.description}</p>
- </div>
- <div className="flex gap-2">
- <button
- onClick={() => handleApprove(approval.id)}
- className="px-3 py-1 font-mono text-[10px] text-[#dbe7ff] bg-green-700 border border-green-600 hover:bg-green-600 uppercase"
- >
- Approve
- </button>
- <button
- onClick={() => handleDeny(approval.id)}
- className="px-3 py-1 font-mono text-[10px] text-[#dbe7ff] bg-red-700 border border-red-600 hover:bg-red-600 uppercase"
- >
- Deny
- </button>
- </div>
- </div>
- </div>
- );
- })}
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/ChainTab.tsx b/makima/frontend/src/components/directives/ChainTab.tsx
deleted file mode 100644
index ccefe81..0000000
--- a/makima/frontend/src/components/directives/ChainTab.tsx
+++ /dev/null
@@ -1,212 +0,0 @@
-import { useState, useMemo } from "react";
-import {
- ReactFlow,
- Edge,
- Controls,
- Background,
- BackgroundVariant,
- MarkerType,
-} from "@xyflow/react";
-import "@xyflow/react/dist/style.css";
-import type { DirectiveWithProgress, DirectiveGraphResponse } from "../../lib/api";
-import { StepNodeComponent, stepStatusStyles } from "./StepNode";
-
-// Node types for React Flow
-const nodeTypes = {
- step: StepNodeComponent,
-};
-
-export function ChainTab({ directive, graph }: { directive: DirectiveWithProgress; graph: DirectiveGraphResponse | null }) {
- const [viewMode, setViewMode] = useState<"dag" | "list">("dag");
-
- // Convert graph to React Flow nodes and edges
- const { nodes, edges } = useMemo(() => {
- if (!graph || !graph.nodes.length) {
- // Fallback: generate positions from directive.steps
- const stepNodes = directive.steps.map((step, index) => ({
- id: step.id,
- type: "step" as const,
- position: {
- x: (index % 3) * 220 + 50,
- y: Math.floor(index / 3) * 120 + 50,
- },
- data: {
- id: step.id,
- name: step.name,
- stepType: step.stepType,
- status: step.status,
- confidenceScore: step.confidenceScore,
- confidenceLevel: step.confidenceLevel,
- contractId: step.contractId,
- editorX: null,
- editorY: null,
- },
- }));
-
- // Build edges from dependencies
- const stepEdges: Edge[] = [];
- directive.steps.forEach((step) => {
- (step.dependsOn ?? []).forEach((depName) => {
- const depStep = directive.steps.find((s) => s.name === depName);
- if (depStep) {
- stepEdges.push({
- id: `${depStep.id}-${step.id}`,
- source: depStep.id,
- target: step.id,
- type: "smoothstep",
- markerEnd: { type: MarkerType.ArrowClosed, color: "#556677" },
- style: { stroke: "#556677", strokeWidth: 2 },
- });
- }
- });
- });
-
- return { nodes: stepNodes, edges: stepEdges };
- }
-
- // Use graph data
- const graphNodes = graph.nodes.map((node) => ({
- id: node.id,
- type: "step" as const,
- position: {
- x: node.editorX ?? 50,
- y: node.editorY ?? 50,
- },
- data: { ...node },
- }));
-
- const graphEdges: Edge[] = graph.edges.map((edge) => ({
- id: `${edge.source}-${edge.target}`,
- source: edge.source,
- target: edge.target,
- type: "smoothstep",
- markerEnd: { type: MarkerType.ArrowClosed, color: "#556677" },
- style: { stroke: "#556677", strokeWidth: 2 },
- }));
-
- return { nodes: graphNodes, edges: graphEdges };
- }, [graph, directive.steps]);
-
- if (!directive.chain) {
- return (
- <div className="text-center py-8">
- <p className="font-mono text-sm text-[#556677]">
- No chain generated yet. Start the directive to generate a chain.
- </p>
- </div>
- );
- }
-
- return (
- <div className="space-y-4">
- {/* Chain info header */}
- <div className="flex items-center justify-between p-3 bg-[rgba(117,170,252,0.05)] border border-[rgba(117,170,252,0.15)]">
- <div>
- <div className="font-mono text-sm text-[#dbe7ff]">{directive.chain.name}</div>
- <div className="font-mono text-[10px] text-[#556677]">Generation {directive.chain.generation}</div>
- </div>
- <div className="flex items-center gap-4">
- <div className="font-mono text-xs text-[#556677]">
- {directive.chain.completedSteps}/{directive.chain.totalSteps} steps
- </div>
- {/* View toggle */}
- <div className="flex border border-[rgba(117,170,252,0.2)]">
- <button
- onClick={() => setViewMode("dag")}
- className={`px-2 py-1 font-mono text-[10px] uppercase ${
- viewMode === "dag"
- ? "bg-[rgba(117,170,252,0.2)] text-[#75aafc]"
- : "text-[#556677] hover:text-[#75aafc]"
- }`}
- >
- DAG
- </button>
- <button
- onClick={() => setViewMode("list")}
- className={`px-2 py-1 font-mono text-[10px] uppercase ${
- viewMode === "list"
- ? "bg-[rgba(117,170,252,0.2)] text-[#75aafc]"
- : "text-[#556677] hover:text-[#75aafc]"
- }`}
- >
- List
- </button>
- </div>
- </div>
- </div>
-
- {viewMode === "dag" ? (
- /* DAG visualization */
- <div className="h-[400px] border border-[rgba(117,170,252,0.1)] rounded bg-[#050d18]">
- {directive.steps.length === 0 ? (
- <div className="flex items-center justify-center h-full">
- <p className="font-mono text-sm text-[#556677]">No steps in chain</p>
- </div>
- ) : (
- <ReactFlow
- nodes={nodes}
- edges={edges}
- nodeTypes={nodeTypes}
- fitView
- fitViewOptions={{ padding: 0.2 }}
- minZoom={0.5}
- maxZoom={1.5}
- defaultEdgeOptions={{
- type: "smoothstep",
- style: { stroke: "#556677", strokeWidth: 2 },
- }}
- >
- <Background variant={BackgroundVariant.Dots} gap={20} size={1} color="#1a2a3a" />
- <Controls className="!bg-[#0a1628] !border-[rgba(117,170,252,0.2)]" />
- </ReactFlow>
- )}
- </div>
- ) : (
- /* List view */
- <div className="space-y-2">
- <h3 className="font-mono text-xs text-[#75aafc] uppercase">Steps</h3>
- {directive.steps.length === 0 ? (
- <p className="font-mono text-sm text-[#556677]">No steps in chain</p>
- ) : (
- directive.steps.map((step) => {
- const styles = stepStatusStyles[step.status] || stepStatusStyles.pending;
-
- return (
- <div
- key={step.id}
- className="p-3 bg-[rgba(117,170,252,0.02)] border border-[rgba(117,170,252,0.1)]"
- >
- <div className="flex items-center justify-between">
- <div className="flex items-center gap-2">
- <span className="font-mono text-sm text-[#dbe7ff]">{step.name}</span>
- <span
- className="px-1.5 py-0.5 font-mono text-[10px] uppercase border"
- style={{ color: styles.text, borderColor: `${styles.border}50` }}
- >
- {step.status}
- </span>
- {step.confidenceScore !== null && (
- <span className="font-mono text-[10px] text-[#556677]">
- ({Math.round(step.confidenceScore * 100)}%)
- </span>
- )}
- </div>
- <div className="font-mono text-[10px] text-[#556677]">{step.stepType}</div>
- </div>
- {step.description && (
- <p className="font-mono text-xs text-[#556677] mt-1">{step.description}</p>
- )}
- {step.dependsOn?.length > 0 && (
- <div className="font-mono text-[10px] text-[#556677] mt-1">
- Depends on: {step.dependsOn.join(", ")}
- </div>
- )}
- </div>
- );
- })
- )}
- </div>
- )}
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/CreateDirectiveModal.tsx b/makima/frontend/src/components/directives/CreateDirectiveModal.tsx
deleted file mode 100644
index 7f52a7e..0000000
--- a/makima/frontend/src/components/directives/CreateDirectiveModal.tsx
+++ /dev/null
@@ -1,146 +0,0 @@
-import { useState, useEffect } from "react";
-import type { AutonomyLevel, RepositoryHistoryEntry } from "../../lib/api";
-import { getRepositorySuggestions } from "../../lib/api";
-
-interface CreateDirectiveModalProps {
- onSubmit: (goal: string, repositoryUrl: string | undefined, autonomyLevel: AutonomyLevel) => void;
- onCancel: () => void;
-}
-
-export function CreateDirectiveModal({ onSubmit, onCancel }: CreateDirectiveModalProps) {
- const [goal, setGoal] = useState("");
- const [repositoryUrl, setRepositoryUrl] = useState("");
- const [autonomyLevel, setAutonomyLevel] = useState<AutonomyLevel>("guardrails");
- const [suggestions, setSuggestions] = useState<RepositoryHistoryEntry[]>([]);
- const [showSuggestions, setShowSuggestions] = useState(false);
-
- // Load suggestions
- useEffect(() => {
- getRepositorySuggestions("remote", undefined, 5)
- .then((res) => {
- setSuggestions(res.entries);
- })
- .catch(() => {
- setSuggestions([]);
- });
- }, []);
-
- const handleSubmit = () => {
- if (goal.trim()) {
- onSubmit(goal.trim(), repositoryUrl.trim() || undefined, autonomyLevel);
- }
- };
-
- return (
- <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
- <div className="w-full max-w-lg p-6 bg-[#0a1628] border border-[rgba(117,170,252,0.3)] max-h-[90vh] overflow-y-auto">
- <h3 className="font-mono text-sm text-[#75aafc] uppercase mb-4">
- Create Directive
- </h3>
-
- <div className="space-y-4">
- {/* Goal */}
- <div>
- <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
- Goal *
- </label>
- <textarea
- value={goal}
- onChange={(e) => setGoal(e.target.value)}
- placeholder="Describe what you want to accomplish..."
- rows={3}
- className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc] resize-none"
- autoFocus
- />
- </div>
-
- {/* Repository URL */}
- <div>
- <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
- Repository URL (optional)
- </label>
- <div className="relative">
- <input
- type="text"
- value={repositoryUrl}
- onChange={(e) => setRepositoryUrl(e.target.value)}
- onFocus={() => suggestions.length > 0 && setShowSuggestions(true)}
- onBlur={() => setTimeout(() => setShowSuggestions(false), 200)}
- placeholder="https://github.com/owner/repo"
- className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
- />
- {showSuggestions && suggestions.length > 0 && (
- <div className="absolute top-full left-0 right-0 mt-1 border border-[rgba(117,170,252,0.2)] bg-[#0a1525] max-h-32 overflow-y-auto z-10">
- {suggestions.map((s) => (
- <button
- key={s.id}
- type="button"
- onClick={() => {
- setRepositoryUrl(s.repositoryUrl || "");
- setShowSuggestions(false);
- }}
- className="w-full text-left px-3 py-2 font-mono text-xs hover:bg-[rgba(117,170,252,0.1)] border-b border-[rgba(117,170,252,0.1)] last:border-b-0"
- >
- <div className="text-[#9bc3ff] truncate">{s.name}</div>
- <div className="text-[10px] text-[#556677] truncate">{s.repositoryUrl}</div>
- </button>
- ))}
- </div>
- )}
- </div>
- </div>
-
- {/* Autonomy Level */}
- <div>
- <label className="block font-mono text-xs text-[#8b949e] uppercase mb-2">
- Autonomy Level
- </label>
- <div className="flex gap-2">
- {(["full_auto", "guardrails", "manual"] as const).map((level) => (
- <button
- key={level}
- type="button"
- onClick={() => setAutonomyLevel(level)}
- className={`flex-1 px-3 py-2 font-mono text-xs uppercase ${
- autonomyLevel === level
- ? "text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3]"
- : "text-[#556677] border border-[rgba(117,170,252,0.2)] hover:border-[#3f6fb3]"
- }`}
- >
- {level.replace("_", " ")}
- </button>
- ))}
- </div>
- <p className="font-mono text-[10px] text-[#556677] mt-1">
- {autonomyLevel === "full_auto" && "Automatic progression without approval gates"}
- {autonomyLevel === "guardrails" && "Request approval for yellow/red confidence scores"}
- {autonomyLevel === "manual" && "Request approval for all step completions"}
- </p>
- </div>
-
- <p className="font-mono text-xs text-[#8b949e]">
- A directive is a top-level goal that generates a chain of steps. Each step spawns
- contracts that are verified before progression.
- </p>
-
- {/* Actions */}
- <div className="flex gap-2 justify-end pt-2">
- <button
- onClick={onCancel}
- className="px-4 py-2 font-mono text-xs text-[#9bc3ff] hover:text-[#dbe7ff] transition-colors"
- >
- Cancel
- </button>
- <button
- onClick={handleSubmit}
- disabled={!goal.trim()}
- className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
- >
- Create
- </button>
- </div>
- </div>
- </div>
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx
deleted file mode 100644
index 06b24bb..0000000
--- a/makima/frontend/src/components/directives/DirectiveDetail.tsx
+++ /dev/null
@@ -1,160 +0,0 @@
-import { useState } from "react";
-import type { DirectiveWithProgress, DirectiveGraphResponse } from "../../lib/api";
-import { OverviewTab } from "./OverviewTab";
-import { ChainTab } from "./ChainTab";
-import { EventsTab } from "./EventsTab";
-import { EvaluationsTab } from "./EvaluationsTab";
-import { ApprovalsTab } from "./ApprovalsTab";
-import { VerifiersTab } from "./VerifiersTab";
-
-interface DirectiveDetailProps {
- directive: DirectiveWithProgress;
- graph: DirectiveGraphResponse | null;
- loading: boolean;
- onBack: () => void;
- onRefresh: () => void;
- onStart: () => void;
- onPause: () => void;
- onResume: () => void;
- onStop: () => void;
-}
-
-export function DirectiveDetail({
- directive,
- graph,
- loading,
- onBack,
- onRefresh,
- onStart,
- onPause,
- onResume,
- onStop,
-}: DirectiveDetailProps) {
- const [activeTab, setActiveTab] = useState<"overview" | "chain" | "events" | "evaluations" | "approvals" | "verifiers">("overview");
-
- if (loading) {
- return (
- <div className="panel h-full flex items-center justify-center">
- <p className="font-mono text-xs text-[#556677]">Loading...</p>
- </div>
- );
- }
-
- const statusColor = {
- draft: "text-[#556677] bg-[#556677]/10 border-[#556677]/30",
- planning: "text-yellow-400 bg-yellow-400/10 border-yellow-400/30",
- active: "text-green-400 bg-green-400/10 border-green-400/30",
- paused: "text-yellow-400 bg-yellow-400/10 border-yellow-400/30",
- completed: "text-[#75aafc] bg-[#75aafc]/10 border-[#75aafc]/30",
- archived: "text-[#556677] bg-[#556677]/10 border-[#556677]/30",
- failed: "text-red-400 bg-red-400/10 border-red-400/30",
- }[directive.status] || "text-[#556677] bg-[#556677]/10 border-[#556677]/30";
-
- return (
- <div className="panel h-full flex flex-col overflow-hidden">
- {/* Header */}
- <div className="p-3 border-b border-[rgba(117,170,252,0.15)]">
- <div className="flex items-center justify-between">
- <div className="flex items-center gap-3">
- <button
- onClick={onBack}
- className="font-mono text-xs text-[#556677] hover:text-[#9bc3ff]"
- >
- &larr; Back
- </button>
- <h2 className="font-mono text-sm text-[#dbe7ff]">
- {directive.title || directive.goal.slice(0, 50)}
- </h2>
- <span className={`px-2 py-0.5 font-mono text-[10px] uppercase border ${statusColor}`}>
- {directive.status}
- </span>
- </div>
- <div className="flex items-center gap-2">
- {directive.status === "draft" && (
- <button
- onClick={onStart}
- className="px-3 py-1 font-mono text-[10px] text-[#dbe7ff] bg-green-700 border border-green-600 hover:bg-green-600 uppercase"
- >
- Start
- </button>
- )}
- {directive.status === "active" && (
- <button
- onClick={onPause}
- className="px-3 py-1 font-mono text-[10px] text-[#dbe7ff] bg-yellow-700 border border-yellow-600 hover:bg-yellow-600 uppercase"
- >
- Pause
- </button>
- )}
- {directive.status === "paused" && (
- <button
- onClick={onResume}
- className="px-3 py-1 font-mono text-[10px] text-[#dbe7ff] bg-green-700 border border-green-600 hover:bg-green-600 uppercase"
- >
- Resume
- </button>
- )}
- {["active", "paused"].includes(directive.status) && (
- <button
- onClick={onStop}
- className="px-3 py-1 font-mono text-[10px] text-[#dbe7ff] bg-red-700 border border-red-600 hover:bg-red-600 uppercase"
- >
- Stop
- </button>
- )}
- <button
- onClick={onRefresh}
- className="px-3 py-1 font-mono text-[10px] text-[#9bc3ff] hover:text-[#dbe7ff]"
- >
- Refresh
- </button>
- </div>
- </div>
- </div>
-
- {/* Tabs */}
- <div className="flex gap-1 p-2 border-b border-[rgba(117,170,252,0.1)]">
- {(["overview", "chain", "events", "evaluations", "approvals", "verifiers"] as const).map((tab) => (
- <button
- key={tab}
- onClick={() => setActiveTab(tab)}
- className={`px-3 py-1.5 font-mono text-[10px] uppercase ${
- activeTab === tab
- ? "text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3]"
- : "text-[#556677] hover:text-[#9bc3ff]"
- }`}
- >
- {tab}
- {tab === "approvals" && directive.pendingApprovals.length > 0 && (
- <span className="ml-1 px-1 bg-yellow-500 text-black rounded text-[8px]">
- {directive.pendingApprovals.length}
- </span>
- )}
- </button>
- ))}
- </div>
-
- {/* Tab Content */}
- <div className="flex-1 overflow-y-auto p-4">
- {activeTab === "overview" && (
- <OverviewTab directive={directive} />
- )}
- {activeTab === "chain" && (
- <ChainTab directive={directive} graph={graph} />
- )}
- {activeTab === "events" && (
- <EventsTab directive={directive} />
- )}
- {activeTab === "evaluations" && (
- <EvaluationsTab directive={directive} />
- )}
- {activeTab === "approvals" && (
- <ApprovalsTab directive={directive} onRefresh={onRefresh} />
- )}
- {activeTab === "verifiers" && (
- <VerifiersTab directive={directive} />
- )}
- </div>
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/DirectiveList.tsx b/makima/frontend/src/components/directives/DirectiveList.tsx
deleted file mode 100644
index d0371e0..0000000
--- a/makima/frontend/src/components/directives/DirectiveList.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { useState } from "react";
-import type { DirectiveSummary } from "../../lib/api";
-import { DirectiveListItem } from "./DirectiveListItem";
-
-interface DirectiveListProps {
- directives: DirectiveSummary[];
- loading: boolean;
- onSelect: (id: string) => void;
- onCreate: () => void;
- selectedId?: string;
- onArchive: (directive: DirectiveSummary) => void;
-}
-
-export function DirectiveList({
- directives,
- loading,
- onSelect,
- onCreate,
- selectedId,
- onArchive,
-}: DirectiveListProps) {
- const [filter, setFilter] = useState<"all" | "active" | "completed" | "failed">("all");
-
- const filteredDirectives = directives.filter((d) => {
- if (filter === "all") return true;
- if (filter === "active") return ["draft", "planning", "active", "paused"].includes(d.status);
- if (filter === "completed") return d.status === "completed";
- if (filter === "failed") return d.status === "failed";
- return true;
- });
-
- return (
- <div className="panel h-full flex flex-col overflow-hidden">
- <div className="flex items-center justify-between p-3 border-b border-[rgba(117,170,252,0.15)]">
- <h2 className="font-mono text-sm text-[#75aafc] uppercase">Directives</h2>
- <button
- onClick={onCreate}
- className="px-3 py-1 font-mono text-[10px] text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors uppercase"
- >
- + New
- </button>
- </div>
-
- {/* Filters */}
- <div className="flex gap-1 p-2 border-b border-[rgba(117,170,252,0.1)]">
- {(["all", "active", "completed", "failed"] as const).map((f) => (
- <button
- key={f}
- onClick={() => setFilter(f)}
- className={`px-2 py-1 font-mono text-[10px] uppercase ${
- filter === f
- ? "text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3]"
- : "text-[#556677] hover:text-[#9bc3ff]"
- }`}
- >
- {f}
- </button>
- ))}
- </div>
-
- {/* List */}
- <div className="flex-1 overflow-y-auto">
- {loading ? (
- <div className="p-4 text-center">
- <p className="font-mono text-xs text-[#556677]">Loading...</p>
- </div>
- ) : filteredDirectives.length === 0 ? (
- <div className="p-4 text-center">
- <p className="font-mono text-xs text-[#556677]">No directives found</p>
- </div>
- ) : (
- filteredDirectives.map((d) => (
- <DirectiveListItem
- key={d.id}
- directive={d}
- selected={d.id === selectedId}
- onClick={() => onSelect(d.id)}
- onArchive={() => onArchive(d)}
- />
- ))
- )}
- </div>
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/DirectiveListItem.tsx b/makima/frontend/src/components/directives/DirectiveListItem.tsx
deleted file mode 100644
index 6ff82e4..0000000
--- a/makima/frontend/src/components/directives/DirectiveListItem.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import type { DirectiveSummary } from "../../lib/api";
-
-interface DirectiveListItemProps {
- directive: DirectiveSummary;
- selected: boolean;
- onClick: () => void;
- onArchive: () => void;
-}
-
-export function DirectiveListItem({ directive, selected, onClick, onArchive }: DirectiveListItemProps) {
- const progress = directive.totalSteps > 0
- ? Math.round((directive.completedSteps / directive.totalSteps) * 100)
- : 0;
-
- const statusColor = {
- draft: "text-[#556677]",
- planning: "text-yellow-400",
- active: "text-green-400",
- paused: "text-yellow-400",
- completed: "text-[#75aafc]",
- archived: "text-[#556677]",
- failed: "text-red-400",
- }[directive.status] || "text-[#556677]";
-
- const confidenceColor = {
- green: "bg-green-500",
- yellow: "bg-yellow-500",
- red: "bg-red-500",
- }[directive.currentConfidence !== null && directive.currentConfidence >= 0.8
- ? "green"
- : directive.currentConfidence !== null && directive.currentConfidence >= 0.5
- ? "yellow"
- : "red"] || "bg-[#556677]";
-
- return (
- <div
- onClick={onClick}
- className={`p-3 cursor-pointer border-b border-[rgba(117,170,252,0.1)] hover:bg-[rgba(117,170,252,0.05)] ${
- selected ? "bg-[rgba(117,170,252,0.1)]" : ""
- }`}
- >
- <div className="flex items-start justify-between gap-2">
- <div className="flex-1 min-w-0">
- <div className="font-mono text-sm text-[#dbe7ff] truncate">
- {directive.title || directive.goal.slice(0, 50)}
- </div>
- <div className="flex items-center gap-2 mt-1">
- <span className={`font-mono text-[10px] uppercase ${statusColor}`}>
- {directive.status}
- </span>
- <span className="font-mono text-[10px] text-[#556677]">
- {directive.completedSteps}/{directive.totalSteps} steps
- </span>
- </div>
- </div>
- <div className="flex flex-col items-end gap-1">
- {directive.currentConfidence !== null && (
- <div className={`w-2 h-2 rounded-full ${confidenceColor}`} title={`Confidence: ${Math.round(directive.currentConfidence * 100)}%`} />
- )}
- <button
- onClick={(e) => {
- e.stopPropagation();
- onArchive();
- }}
- className="font-mono text-[10px] text-[#556677] hover:text-red-400"
- >
- Archive
- </button>
- </div>
- </div>
-
- {/* Progress bar */}
- {directive.totalSteps > 0 && (
- <div className="mt-2 h-1 bg-[rgba(117,170,252,0.1)] overflow-hidden">
- <div
- className="h-full bg-[#75aafc] transition-all duration-300"
- style={{ width: `${progress}%` }}
- />
- </div>
- )}
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/EvaluationsTab.tsx b/makima/frontend/src/components/directives/EvaluationsTab.tsx
deleted file mode 100644
index c1d65db..0000000
--- a/makima/frontend/src/components/directives/EvaluationsTab.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { DirectiveWithProgress } from "../../lib/api";
-
-export function EvaluationsTab({ directive: _directive }: { directive: DirectiveWithProgress }) {
- // TODO: Fetch evaluations separately
- return (
- <div className="text-center py-8">
- <p className="font-mono text-sm text-[#556677]">
- Evaluations will be shown here after steps are evaluated
- </p>
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/EventsTab.tsx b/makima/frontend/src/components/directives/EventsTab.tsx
deleted file mode 100644
index 4dd739a..0000000
--- a/makima/frontend/src/components/directives/EventsTab.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { useMemo } from "react";
-import type { DirectiveWithProgress } from "../../lib/api";
-import { useDirectiveEventSubscription } from "../../hooks/useDirectives";
-
-export function EventsTab({ directive }: { directive: DirectiveWithProgress }) {
- // Subscribe to real-time events via SSE
- const { events: streamEvents, isConnected, error: sseError } = useDirectiveEventSubscription(directive.id);
-
- // Combine initial events with streamed events (avoiding duplicates)
- const allEvents = useMemo(() => {
- const eventMap = new Map();
- // Add initial events first
- directive.recentEvents.forEach((e) => eventMap.set(e.id, e));
- // Add streamed events (will override any duplicates)
- streamEvents.forEach((e) => eventMap.set(e.id, e));
- // Sort by created_at descending (most recent first)
- return Array.from(eventMap.values()).sort(
- (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
- );
- }, [directive.recentEvents, streamEvents]);
-
- return (
- <div className="space-y-4">
- {/* Connection status */}
- <div className="flex items-center justify-between text-[10px] font-mono">
- <div className="flex items-center gap-2">
- <span className={isConnected ? "text-green-400" : "text-[#556677]"}>
- {isConnected ? "\u25CF Live" : "\u25CB Connecting..."}
- </span>
- {sseError && <span className="text-red-400">{sseError}</span>}
- </div>
- <span className="text-[#556677]">{allEvents.length} events</span>
- </div>
-
- {/* Event list */}
- {allEvents.length === 0 ? (
- <div className="text-center py-8">
- <p className="font-mono text-sm text-[#556677]">No events yet</p>
- </div>
- ) : (
- <div className="space-y-2">
- {allEvents.map((event) => {
- const severityColors: Record<string, string> = {
- info: "text-[#75aafc]",
- warning: "text-yellow-400",
- error: "text-red-400",
- critical: "text-red-600",
- };
- const severityColor = severityColors[event.severity] || "text-[#556677]";
-
- return (
- <div
- key={event.id}
- className="p-3 bg-[rgba(117,170,252,0.02)] border border-[rgba(117,170,252,0.1)]"
- >
- <div className="flex items-center justify-between">
- <div className="flex items-center gap-2">
- <span className={`font-mono text-xs ${severityColor}`}>{event.eventType}</span>
- <span className="font-mono text-[10px] text-[#556677]">{event.actorType}</span>
- </div>
- <span className="font-mono text-[10px] text-[#556677]">
- {new Date(event.createdAt).toLocaleString()}
- </span>
- </div>
- {event.eventData != null && (
- <pre className="font-mono text-[10px] text-[#556677] mt-1 overflow-x-auto">
- {JSON.stringify(event.eventData, null, 2)}
- </pre>
- )}
- </div>
- );
- })}
- </div>
- )}
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/OverviewTab.tsx b/makima/frontend/src/components/directives/OverviewTab.tsx
deleted file mode 100644
index 41cd7dc..0000000
--- a/makima/frontend/src/components/directives/OverviewTab.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import type { DirectiveWithProgress } from "../../lib/api";
-
-export function OverviewTab({ directive }: { directive: DirectiveWithProgress }) {
- return (
- <div className="space-y-6">
- {/* Goal */}
- <div>
- <h3 className="font-mono text-xs text-[#75aafc] uppercase mb-2">Goal</h3>
- <p className="font-mono text-sm text-[#dbe7ff] whitespace-pre-wrap">
- {directive.goal}
- </p>
- </div>
-
- {/* Progress */}
- <div>
- <h3 className="font-mono text-xs text-[#75aafc] uppercase mb-2">Progress</h3>
- <div className="grid grid-cols-3 gap-4">
- <div className="p-3 bg-[rgba(117,170,252,0.05)] border border-[rgba(117,170,252,0.15)]">
- <div className="font-mono text-2xl text-[#dbe7ff]">
- {directive.chain?.completedSteps || 0}
- </div>
- <div className="font-mono text-[10px] text-[#556677] uppercase">Completed Steps</div>
- </div>
- <div className="p-3 bg-[rgba(117,170,252,0.05)] border border-[rgba(117,170,252,0.15)]">
- <div className="font-mono text-2xl text-[#dbe7ff]">
- {directive.chain?.totalSteps || 0}
- </div>
- <div className="font-mono text-[10px] text-[#556677] uppercase">Total Steps</div>
- </div>
- <div className="p-3 bg-[rgba(117,170,252,0.05)] border border-[rgba(117,170,252,0.15)]">
- <div className="font-mono text-2xl text-[#dbe7ff]">
- {directive.chain?.currentConfidence != null
- ? `${Math.round((directive.chain?.currentConfidence ?? 0) * 100)}%`
- : "-"}
- </div>
- <div className="font-mono text-[10px] text-[#556677] uppercase">Confidence</div>
- </div>
- </div>
- </div>
-
- {/* Configuration */}
- <div>
- <h3 className="font-mono text-xs text-[#75aafc] uppercase mb-2">Configuration</h3>
- <div className="grid grid-cols-2 gap-2 text-sm">
- <div className="flex justify-between">
- <span className="font-mono text-[#556677]">Autonomy Level</span>
- <span className="font-mono text-[#dbe7ff]">{directive.autonomyLevel}</span>
- </div>
- <div className="flex justify-between">
- <span className="font-mono text-[#556677]">Max Rework Cycles</span>
- <span className="font-mono text-[#dbe7ff]">{directive.maxReworkCycles}</span>
- </div>
- <div className="flex justify-between">
- <span className="font-mono text-[#556677]">Green Threshold</span>
- <span className="font-mono text-[#dbe7ff]">{directive.confidenceThresholdGreen}</span>
- </div>
- <div className="flex justify-between">
- <span className="font-mono text-[#556677]">Yellow Threshold</span>
- <span className="font-mono text-[#dbe7ff]">{directive.confidenceThresholdYellow}</span>
- </div>
- </div>
- </div>
-
- {/* Repository */}
- {directive.repositoryUrl && (
- <div>
- <h3 className="font-mono text-xs text-[#75aafc] uppercase mb-2">Repository</h3>
- <p className="font-mono text-sm text-[#9bc3ff]">{directive.repositoryUrl}</p>
- </div>
- )}
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/StepNode.tsx b/makima/frontend/src/components/directives/StepNode.tsx
deleted file mode 100644
index e54f5eb..0000000
--- a/makima/frontend/src/components/directives/StepNode.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import { Handle, Position } from "@xyflow/react";
-import type { StepStatus, ConfidenceLevel, DirectiveGraphNode } from "../../lib/api";
-
-// Step status colors for both list and DAG views
-export const stepStatusStyles: Record<StepStatus, { border: string; bg: string; text: string }> = {
- pending: { border: "#556677", bg: "#556677", text: "#556677" },
- ready: { border: "#3b82f6", bg: "#3b82f6", text: "#3b82f6" },
- running: { border: "#22c55e", bg: "#22c55e", text: "#22c55e" },
- evaluating: { border: "#eab308", bg: "#eab308", text: "#eab308" },
- passed: { border: "#75aafc", bg: "#75aafc", text: "#75aafc" },
- failed: { border: "#ef4444", bg: "#ef4444", text: "#ef4444" },
- rework: { border: "#f97316", bg: "#f97316", text: "#f97316" },
- skipped: { border: "#556677", bg: "#556677", text: "#556677" },
- blocked: { border: "#ef4444", bg: "#ef4444", text: "#ef4444" },
-};
-
-// Confidence level colors
-export const confidenceColors: Record<ConfidenceLevel, string> = {
- green: "#22c55e",
- yellow: "#eab308",
- red: "#ef4444",
-};
-
-// Node dimensions
-export const NODE_WIDTH = 180;
-export const NODE_HEIGHT = 70;
-
-// Custom node component for steps
-export function StepNodeComponent({ data }: { data: DirectiveGraphNode & { selected?: boolean } }) {
- const styles = stepStatusStyles[data.status] || stepStatusStyles.pending;
- const isRunning = data.status === "running" || data.status === "evaluating";
-
- return (
- <div
- className={`rounded-lg border-2 bg-[#0a1628] overflow-hidden ${
- isRunning ? "animate-pulse" : ""
- }`}
- style={{
- width: NODE_WIDTH,
- height: NODE_HEIGHT,
- borderColor: styles.border,
- borderStyle: data.status === "pending" ? "dashed" : "solid",
- }}
- >
- <Handle
- type="target"
- position={Position.Top}
- className="!bg-[#75aafc] !w-3 !h-3 !border-2 !border-[#0a1628]"
- />
-
- {/* Status indicator bar */}
- <div className="h-1.5" style={{ backgroundColor: styles.bg }} />
-
- {/* Content */}
- <div className="p-2">
- <div className="flex items-center justify-between mb-1">
- <span className="font-mono text-xs text-[#dbe7ff] truncate flex-1">{data.name}</span>
- {data.confidenceScore !== null && data.confidenceLevel && (
- <div
- className="w-2 h-2 rounded-full flex-shrink-0 ml-1"
- style={{ backgroundColor: confidenceColors[data.confidenceLevel] }}
- title={`Confidence: ${Math.round(data.confidenceScore * 100)}%`}
- />
- )}
- </div>
- <div className="flex items-center justify-between">
- <span
- className="font-mono text-[10px] uppercase px-1.5 py-0.5 rounded"
- style={{
- color: styles.text,
- backgroundColor: `${styles.bg}20`,
- }}
- >
- {data.status}
- </span>
- <span className="font-mono text-[10px] text-[#8b949e]">{data.stepType}</span>
- </div>
- </div>
-
- <Handle
- type="source"
- position={Position.Bottom}
- className="!bg-[#f59e0b] !w-3 !h-3 !border-2 !border-[#0a1628]"
- />
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/VerifiersTab.tsx b/makima/frontend/src/components/directives/VerifiersTab.tsx
deleted file mode 100644
index cfcfdd8..0000000
--- a/makima/frontend/src/components/directives/VerifiersTab.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { DirectiveWithProgress } from "../../lib/api";
-
-export function VerifiersTab({ directive: _directive }: { directive: DirectiveWithProgress }) {
- // TODO: Fetch verifiers separately
- return (
- <div className="text-center py-8">
- <p className="font-mono text-sm text-[#556677]">
- Verifiers will be shown here. Use auto-detect to find available verifiers.
- </p>
- </div>
- );
-}
diff --git a/makima/frontend/src/components/directives/index.ts b/makima/frontend/src/components/directives/index.ts
deleted file mode 100644
index 718b1f2..0000000
--- a/makima/frontend/src/components/directives/index.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export { DirectiveList } from "./DirectiveList";
-export { DirectiveListItem } from "./DirectiveListItem";
-export { DirectiveDetail } from "./DirectiveDetail";
-export { OverviewTab } from "./OverviewTab";
-export { ChainTab } from "./ChainTab";
-export { EventsTab } from "./EventsTab";
-export { EvaluationsTab } from "./EvaluationsTab";
-export { ApprovalsTab } from "./ApprovalsTab";
-export { VerifiersTab } from "./VerifiersTab";
-export { CreateDirectiveModal } from "./CreateDirectiveModal";
-export { StepNodeComponent, stepStatusStyles, confidenceColors, NODE_WIDTH, NODE_HEIGHT } from "./StepNode";
diff --git a/makima/frontend/src/hooks/useDirectiveDetail.ts b/makima/frontend/src/hooks/useDirectiveDetail.ts
deleted file mode 100644
index 1167242..0000000
--- a/makima/frontend/src/hooks/useDirectiveDetail.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-import { useState, useCallback, useEffect } from "react";
-import {
- getDirective,
- getDirectiveGraph,
- startDirective,
- pauseDirective,
- resumeDirective,
- stopDirective,
- type DirectiveWithProgress,
- type DirectiveGraphResponse,
- type StartDirectiveResponse,
-} from "../lib/api";
-
-interface UseDirectiveDetailResult {
- directive: DirectiveWithProgress | null;
- graph: DirectiveGraphResponse | null;
- loading: boolean;
- error: string | null;
- refresh: () => Promise<void>;
- start: () => Promise<StartDirectiveResponse | null>;
- pause: () => Promise<boolean>;
- resume: () => Promise<boolean>;
- stop: () => Promise<boolean>;
-}
-
-export function useDirectiveDetail(directiveId: string | undefined): UseDirectiveDetailResult {
- const [directive, setDirective] = useState<DirectiveWithProgress | null>(null);
- const [graph, setGraph] = useState<DirectiveGraphResponse | null>(null);
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState<string | null>(null);
-
- const fetchDetail = useCallback(async () => {
- if (!directiveId) {
- setDirective(null);
- setGraph(null);
- return;
- }
-
- setLoading(true);
- setError(null);
- try {
- const [d, g] = await Promise.all([
- getDirective(directiveId),
- getDirectiveGraph(directiveId).catch(() => null),
- ]);
- setDirective(d);
- setGraph(g);
- } catch (err) {
- console.error("Failed to fetch directive detail:", err);
- setError(err instanceof Error ? err.message : "Failed to fetch directive");
- setDirective(null);
- setGraph(null);
- } finally {
- setLoading(false);
- }
- }, [directiveId]);
-
- useEffect(() => {
- fetchDetail();
- }, [fetchDetail]);
-
- const start = useCallback(async (): Promise<StartDirectiveResponse | null> => {
- if (!directiveId) return null;
- try {
- const response = await startDirective(directiveId);
- await fetchDetail();
- return response;
- } catch (err) {
- console.error("Failed to start directive:", err);
- setError(err instanceof Error ? err.message : "Failed to start directive");
- return null;
- }
- }, [directiveId, fetchDetail]);
-
- const pause = useCallback(async (): Promise<boolean> => {
- if (!directiveId) return false;
- try {
- await pauseDirective(directiveId);
- await fetchDetail();
- return true;
- } catch (err) {
- console.error("Failed to pause directive:", err);
- setError(err instanceof Error ? err.message : "Failed to pause directive");
- return false;
- }
- }, [directiveId, fetchDetail]);
-
- const resume = useCallback(async (): Promise<boolean> => {
- if (!directiveId) return false;
- try {
- await resumeDirective(directiveId);
- await fetchDetail();
- return true;
- } catch (err) {
- console.error("Failed to resume directive:", err);
- setError(err instanceof Error ? err.message : "Failed to resume directive");
- return false;
- }
- }, [directiveId, fetchDetail]);
-
- const stop = useCallback(async (): Promise<boolean> => {
- if (!directiveId) return false;
- try {
- await stopDirective(directiveId);
- await fetchDetail();
- return true;
- } catch (err) {
- console.error("Failed to stop directive:", err);
- setError(err instanceof Error ? err.message : "Failed to stop directive");
- return false;
- }
- }, [directiveId, fetchDetail]);
-
- return {
- directive,
- graph,
- loading,
- error,
- refresh: fetchDetail,
- start,
- pause,
- resume,
- stop,
- };
-}
diff --git a/makima/frontend/src/hooks/useDirectives.ts b/makima/frontend/src/hooks/useDirectives.ts
deleted file mode 100644
index 7ae24a5..0000000
--- a/makima/frontend/src/hooks/useDirectives.ts
+++ /dev/null
@@ -1,298 +0,0 @@
-import { useState, useCallback, useEffect, useRef } from "react";
-import {
- listDirectives,
- getDirective,
- createDirective,
- updateDirective,
- archiveDirective,
- startDirective,
- pauseDirective,
- resumeDirective,
- stopDirective,
- getDirectiveGraph,
- subscribeToDirectiveEvents,
- type DirectiveSummary,
- type DirectiveWithProgress,
- type DirectiveGraphResponse,
- type DirectiveStatus,
- type DirectiveEvent,
- type CreateDirectiveRequest,
- type UpdateDirectiveRequest,
- type StartDirectiveResponse,
-} from "../lib/api";
-
-interface UseDirectivesResult {
- directives: DirectiveSummary[];
- loading: boolean;
- error: string | null;
- refresh: () => Promise<void>;
- createNewDirective: (req: CreateDirectiveRequest) => Promise<DirectiveWithProgress | null>;
- updateExistingDirective: (
- directiveId: string,
- req: UpdateDirectiveRequest
- ) => Promise<DirectiveWithProgress | null>;
- archiveExistingDirective: (directiveId: string) => Promise<boolean>;
- getDirectiveById: (directiveId: string) => Promise<DirectiveWithProgress | null>;
- getGraph: (directiveId: string) => Promise<DirectiveGraphResponse | null>;
- start: (directiveId: string) => Promise<StartDirectiveResponse | null>;
- pause: (directiveId: string) => Promise<boolean>;
- resume: (directiveId: string) => Promise<boolean>;
- stop: (directiveId: string) => Promise<boolean>;
-}
-
-export function useDirectives(statusFilter?: DirectiveStatus): UseDirectivesResult {
- const [directives, setDirectives] = useState<DirectiveSummary[]>([]);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState<string | null>(null);
-
- const fetchDirectives = useCallback(async () => {
- setLoading(true);
- setError(null);
- try {
- const response = await listDirectives(statusFilter);
- setDirectives(response.directives ?? []);
- } catch (err) {
- console.error("Failed to fetch directives:", err);
- setError(err instanceof Error ? err.message : "Failed to fetch directives");
- } finally {
- setLoading(false);
- }
- }, [statusFilter]);
-
- useEffect(() => {
- fetchDirectives();
- }, [fetchDirectives]);
-
- const createNewDirective = useCallback(
- async (req: CreateDirectiveRequest): Promise<DirectiveWithProgress | null> => {
- try {
- const directive = await createDirective(req);
- // Refresh the list
- await fetchDirectives();
- // Return the full directive with progress
- return await getDirective(directive.id);
- } catch (err) {
- console.error("Failed to create directive:", err);
- setError(err instanceof Error ? err.message : "Failed to create directive");
- return null;
- }
- },
- [fetchDirectives]
- );
-
- const updateExistingDirective = useCallback(
- async (
- directiveId: string,
- req: UpdateDirectiveRequest
- ): Promise<DirectiveWithProgress | null> => {
- try {
- await updateDirective(directiveId, req);
- // Refresh the list
- await fetchDirectives();
- // Return the updated directive
- return await getDirective(directiveId);
- } catch (err) {
- console.error("Failed to update directive:", err);
- setError(err instanceof Error ? err.message : "Failed to update directive");
- return null;
- }
- },
- [fetchDirectives]
- );
-
- const archiveExistingDirective = useCallback(
- async (directiveId: string): Promise<boolean> => {
- try {
- await archiveDirective(directiveId);
- // Refresh the list
- await fetchDirectives();
- return true;
- } catch (err) {
- console.error("Failed to archive directive:", err);
- setError(err instanceof Error ? err.message : "Failed to archive directive");
- return false;
- }
- },
- [fetchDirectives]
- );
-
- const getDirectiveById = useCallback(
- async (directiveId: string): Promise<DirectiveWithProgress | null> => {
- try {
- return await getDirective(directiveId);
- } catch (err) {
- console.error("Failed to get directive:", err);
- setError(err instanceof Error ? err.message : "Failed to get directive");
- return null;
- }
- },
- []
- );
-
- const getGraph = useCallback(
- async (directiveId: string): Promise<DirectiveGraphResponse | null> => {
- try {
- return await getDirectiveGraph(directiveId);
- } catch (err) {
- console.error("Failed to get directive graph:", err);
- setError(err instanceof Error ? err.message : "Failed to get directive graph");
- return null;
- }
- },
- []
- );
-
- const start = useCallback(
- async (directiveId: string): Promise<StartDirectiveResponse | null> => {
- try {
- const response = await startDirective(directiveId);
- await fetchDirectives();
- return response;
- } catch (err) {
- console.error("Failed to start directive:", err);
- setError(err instanceof Error ? err.message : "Failed to start directive");
- return null;
- }
- },
- [fetchDirectives]
- );
-
- const pause = useCallback(
- async (directiveId: string): Promise<boolean> => {
- try {
- await pauseDirective(directiveId);
- await fetchDirectives();
- return true;
- } catch (err) {
- console.error("Failed to pause directive:", err);
- setError(err instanceof Error ? err.message : "Failed to pause directive");
- return false;
- }
- },
- [fetchDirectives]
- );
-
- const resume = useCallback(
- async (directiveId: string): Promise<boolean> => {
- try {
- await resumeDirective(directiveId);
- await fetchDirectives();
- return true;
- } catch (err) {
- console.error("Failed to resume directive:", err);
- setError(err instanceof Error ? err.message : "Failed to resume directive");
- return false;
- }
- },
- [fetchDirectives]
- );
-
- const stop = useCallback(
- async (directiveId: string): Promise<boolean> => {
- try {
- await stopDirective(directiveId);
- await fetchDirectives();
- return true;
- } catch (err) {
- console.error("Failed to stop directive:", err);
- setError(err instanceof Error ? err.message : "Failed to stop directive");
- return false;
- }
- },
- [fetchDirectives]
- );
-
- return {
- directives,
- loading,
- error,
- refresh: fetchDirectives,
- createNewDirective,
- updateExistingDirective,
- archiveExistingDirective,
- getDirectiveById,
- getGraph,
- start,
- pause,
- resume,
- stop,
- };
-}
-
-/** Hook for subscribing to real-time directive events via SSE */
-export function useDirectiveEventSubscription(
- directiveId: string | null,
- onEvent?: (event: DirectiveEvent) => void
-): {
- events: DirectiveEvent[];
- isConnected: boolean;
- error: string | null;
-} {
- const [events, setEvents] = useState<DirectiveEvent[]>([]);
- const [isConnected, setIsConnected] = useState(false);
- const [error, setError] = useState<string | null>(null);
- const cleanupRef = useRef<(() => void) | null>(null);
-
- useEffect(() => {
- // Clean up any existing subscription
- if (cleanupRef.current) {
- cleanupRef.current();
- cleanupRef.current = null;
- }
-
- if (!directiveId) {
- setIsConnected(false);
- setEvents([]);
- return;
- }
-
- // Subscribe to events
- let mounted = true;
-
- const setupSubscription = async () => {
- try {
- const cleanup = await subscribeToDirectiveEvents(
- directiveId,
- (event) => {
- if (mounted) {
- setEvents((prev) => [...prev, event]);
- onEvent?.(event);
- }
- },
- (err) => {
- if (mounted) {
- setError(err.message);
- setIsConnected(false);
- }
- }
- );
-
- if (mounted) {
- cleanupRef.current = cleanup;
- setIsConnected(true);
- setError(null);
- } else {
- // Component unmounted during setup, clean up immediately
- cleanup();
- }
- } catch (err) {
- if (mounted) {
- setError(err instanceof Error ? err.message : "Failed to subscribe to events");
- setIsConnected(false);
- }
- }
- };
-
- setupSubscription();
-
- return () => {
- mounted = false;
- if (cleanupRef.current) {
- cleanupRef.current();
- cleanupRef.current = null;
- }
- };
- }, [directiveId, onEvent]);
-
- return { events, isConnected, error };
-}
diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts
index 466a794..9f5ff88 100644
--- a/makima/frontend/src/lib/api.ts
+++ b/makima/frontend/src/lib/api.ts
@@ -3003,1286 +3003,3 @@ export async function listTaskPatches(taskId: string, contractId: string): Promi
return res.json();
}
-// =============================================================================
-// Chain Types and API
-// =============================================================================
-
-/** Chain status */
-export type ChainStatus = "pending" | "active" | "completed" | "archived";
-
-/** Chain summary for list view */
-export interface ChainSummary {
- id: string;
- name: string;
- description: string | null;
- status: ChainStatus;
- contractCount: number;
- completedContractCount: number;
- loopEnabled: boolean;
- loopCurrentIteration: number | null;
- loopMaxIterations: number | null;
- createdAt: string;
- updatedAt: string;
-}
-
-/** Chain repository */
-export interface ChainRepository {
- id: string;
- chainId: string;
- name: string;
- repositoryUrl: string | null;
- localPath: string | null;
- sourceType: string;
- status: string;
- isPrimary: boolean;
- createdAt: string;
- updatedAt: string;
-}
-
-/** Full chain with contracts */
-export interface Chain {
- id: string;
- ownerId: string;
- name: string;
- description: string | null;
- status: ChainStatus;
- loopEnabled: boolean;
- loopMaxIterations: number | null;
- loopCurrentIteration: number | null;
- loopProgressCheck: string | null;
- version: number;
- createdAt: string;
- updatedAt: string;
-}
-
-/** Contract detail within a chain */
-export interface ChainContractDetail {
- id: string;
- chainId: string;
- contractId: string;
- contractName: string;
- contractStatus: string;
- contractPhase: string;
- dependsOn: string[];
- orderIndex: number;
- editorX: number | null;
- editorY: number | null;
- createdAt: string;
-}
-
-/** Chain with contracts (chain fields are flattened via serde(flatten)) */
-export interface ChainWithContracts extends Chain {
- contracts: ChainContractDetail[];
- repositories: ChainRepository[];
-}
-
-/** Node in chain graph visualization */
-export interface ChainGraphNode {
- id: string;
- contractId: string;
- name: string;
- status: string;
- phase: string;
- x: number;
- y: number;
-}
-
-/** Edge in chain graph */
-export interface ChainGraphEdge {
- from: string;
- to: string;
-}
-
-/** Chain graph response */
-export interface ChainGraphResponse {
- chainId: string;
- chainName: string;
- chainStatus: string;
- nodes: ChainGraphNode[];
- edges: ChainGraphEdge[];
-}
-
-/** Chain event */
-export interface ChainEvent {
- id: string;
- chainId: string;
- eventType: string;
- contractId: string | null;
- eventData: Record<string, unknown> | null;
- createdAt: string;
-}
-
-/** Chain list response */
-export interface ChainListResponse {
- chains: ChainSummary[];
- total: number;
-}
-
-/** Add chain repository request */
-export interface AddChainRepositoryRequest {
- name: string;
- repositoryUrl?: string;
- localPath?: string;
- sourceType?: string;
- isPrimary?: boolean;
-}
-
-/** Create chain request */
-export interface CreateChainRequest {
- name: string;
- description?: string;
- repositoryUrl?: string; // Legacy field for backwards compatibility
- repositories?: AddChainRepositoryRequest[];
- loopEnabled?: boolean;
- loopMaxIterations?: number;
- loopProgressCheck?: string;
- contracts?: CreateChainContractRequest[];
-}
-
-/** Create chain contract request */
-export interface CreateChainContractRequest {
- name: string;
- description?: string;
- contractType?: string;
- initialPhase?: string;
- phases?: string[];
- dependsOn?: string[];
- tasks?: { name: string; plan: string }[];
- deliverables?: { id: string; name: string; priority?: string }[];
- editorX?: number;
- editorY?: number;
-}
-
-/** Update chain request */
-export interface UpdateChainRequest {
- name?: string;
- description?: string;
- status?: ChainStatus;
- loopEnabled?: boolean;
- loopMaxIterations?: number;
- loopProgressCheck?: string;
- version?: number;
-}
-
-/** List chains */
-export async function listChains(
- status?: ChainStatus,
- limit = 50,
- offset = 0
-): Promise<ChainListResponse> {
- const params = new URLSearchParams();
- if (status) params.set("status", status);
- params.set("limit", String(limit));
- params.set("offset", String(offset));
-
- const res = await authFetch(`${API_BASE}/api/v1/chains?${params}`);
- if (!res.ok) {
- throw new Error(`Failed to list chains: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Get chain by ID */
-export async function getChain(chainId: string): Promise<ChainWithContracts> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}`);
- if (!res.ok) {
- throw new Error(`Failed to get chain: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Create a new chain */
-export async function createChain(req: CreateChainRequest): Promise<Chain> {
- const res = await authFetch(`${API_BASE}/api/v1/chains`, {
- method: "POST",
- body: JSON.stringify(req),
- });
- if (!res.ok) {
- const errorText = await res.text();
- throw new Error(`Failed to create chain: ${errorText || res.statusText}`);
- }
- return res.json();
-}
-
-/** Update a chain */
-export async function updateChain(
- chainId: string,
- req: UpdateChainRequest
-): Promise<Chain> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}`, {
- method: "PUT",
- body: JSON.stringify(req),
- });
- if (!res.ok) {
- const errorText = await res.text();
- throw new Error(`Failed to update chain: ${errorText || res.statusText}`);
- }
- return res.json();
-}
-
-/** Archive a chain */
-export async function archiveChain(chainId: string): Promise<{ archived: boolean }> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}`, {
- method: "DELETE",
- });
- if (!res.ok) {
- throw new Error(`Failed to archive chain: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Get chain contracts */
-export async function getChainContracts(
- chainId: string
-): Promise<ChainContractDetail[]> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/contracts`);
- if (!res.ok) {
- throw new Error(`Failed to get chain contracts: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Get chain graph for visualization */
-export async function getChainGraph(chainId: string): Promise<ChainGraphResponse> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/graph`);
- if (!res.ok) {
- throw new Error(`Failed to get chain graph: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Get chain events */
-export async function getChainEvents(chainId: string): Promise<ChainEvent[]> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/events`);
- if (!res.ok) {
- throw new Error(`Failed to get chain events: ${res.statusText}`);
- }
- return res.json();
-}
-
-// =============================================================================
-// Chain Contract Definitions
-// =============================================================================
-
-/** Task definition for chain contract definitions */
-export interface ChainTaskDefinition {
- name: string;
- plan: string;
-}
-
-/** Deliverable definition for chain contract definitions (optional priority) */
-export interface ChainDeliverableDefinition {
- id: string;
- name: string;
- priority?: string;
-}
-
-/** Validation configuration for checkpoint contracts */
-export interface CheckpointValidation {
- /** Check that all required deliverables from upstream contracts exist */
- checkDeliverables?: boolean;
- /** Run tests in the repository */
- runTests?: boolean;
- /** Custom validation instructions for Claude */
- checkContent?: string;
- /** Action on failure: "block", "retry", "warn" */
- onFailure?: "block" | "retry" | "warn";
- /** Max retry attempts for upstream contracts */
- maxRetries?: number;
-}
-
-/** Contract definition stored in chain (before actual contract is created) */
-export interface ChainContractDefinition {
- id: string;
- chainId: string;
- name: string;
- description: string | null;
- contractType: string;
- initialPhase: string | null;
- dependsOnNames: string[];
- tasks: ChainTaskDefinition[] | null;
- deliverables: ChainDeliverableDefinition[] | null;
- /** Validation config for checkpoint contracts */
- validation: CheckpointValidation | null;
- editorX: number | null;
- editorY: number | null;
- orderIndex: number;
- createdAt: string;
-}
-
-/** Request to add a contract definition to a chain */
-export interface AddContractDefinitionRequest {
- name: string;
- description?: string;
- contractType?: string;
- initialPhase?: string;
- dependsOn?: string[];
- tasks?: ChainTaskDefinition[];
- deliverables?: ChainDeliverableDefinition[];
- /** Validation config (for checkpoint contracts) */
- validation?: CheckpointValidation;
- editorX?: number;
- editorY?: number;
- orderIndex?: number;
-}
-
-/** Request to update a contract definition */
-export interface UpdateContractDefinitionRequest {
- name?: string;
- description?: string;
- contractType?: string;
- initialPhase?: string;
- dependsOn?: string[];
- tasks?: ChainTaskDefinition[];
- deliverables?: ChainDeliverableDefinition[];
- /** Validation config (for checkpoint contracts) */
- validation?: CheckpointValidation;
- editorX?: number;
- editorY?: number;
- orderIndex?: number;
-}
-
-/** Response when starting a chain */
-export interface StartChainResponse {
- chainId: string;
- contractsCreated: string[];
- status: string;
-}
-
-/** Node in definition graph (shows definitions + instantiation status) */
-export interface ChainDefinitionGraphNode {
- id: string;
- name: string;
- contractType: string;
- x: number;
- y: number;
- isInstantiated: boolean;
- contractId: string | null;
- contractStatus: string | null;
-}
-
-/** Definition graph response */
-export interface ChainDefinitionGraphResponse {
- chainId: string;
- chainName: string;
- chainStatus: string;
- nodes: ChainDefinitionGraphNode[];
- edges: ChainGraphEdge[];
-}
-
-/** List contract definitions for a chain */
-export async function listChainDefinitions(
- chainId: string
-): Promise<ChainContractDefinition[]> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/definitions`);
- if (!res.ok) {
- throw new Error(`Failed to list chain definitions: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Create a contract definition for a chain */
-export async function createChainDefinition(
- chainId: string,
- req: AddContractDefinitionRequest
-): Promise<ChainContractDefinition> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/definitions`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(req),
- });
- if (!res.ok) {
- throw new Error(`Failed to create chain definition: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Update a contract definition */
-export async function updateChainDefinition(
- chainId: string,
- definitionId: string,
- req: UpdateContractDefinitionRequest
-): Promise<ChainContractDefinition> {
- const res = await authFetch(
- `${API_BASE}/api/v1/chains/${chainId}/definitions/${definitionId}`,
- {
- method: "PUT",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(req),
- }
- );
- if (!res.ok) {
- throw new Error(`Failed to update chain definition: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Delete a contract definition */
-export async function deleteChainDefinition(
- chainId: string,
- definitionId: string
-): Promise<{ deleted: boolean }> {
- const res = await authFetch(
- `${API_BASE}/api/v1/chains/${chainId}/definitions/${definitionId}`,
- { method: "DELETE" }
- );
- if (!res.ok) {
- throw new Error(`Failed to delete chain definition: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Get definition graph for a chain */
-export async function getChainDefinitionGraph(
- chainId: string
-): Promise<ChainDefinitionGraphResponse> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/definitions/graph`);
- if (!res.ok) {
- throw new Error(`Failed to get chain definition graph: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Start a chain (creates root contracts based on DAG) */
-export async function startChain(chainId: string): Promise<StartChainResponse> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/start`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({}),
- });
- if (!res.ok) {
- const error = await res.json().catch(() => ({ message: res.statusText }));
- throw new Error(error.message || `Failed to start chain: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Stop a chain (marks as archived) */
-export async function stopChain(chainId: string): Promise<{ stopped: boolean; status: string }> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/stop`, {
- method: "POST",
- });
- if (!res.ok) {
- const error = await res.json().catch(() => ({ message: res.statusText }));
- throw new Error(error.message || `Failed to stop chain: ${res.statusText}`);
- }
- return res.json();
-}
-
-// ============================================================================
-// Chain Repository Operations
-// ============================================================================
-
-/** List repositories for a chain */
-export async function listChainRepositories(chainId: string): Promise<ChainRepository[]> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/repositories`);
- if (!res.ok) {
- throw new Error(`Failed to list chain repositories: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Add a repository to a chain */
-export async function addChainRepository(
- chainId: string,
- req: AddChainRepositoryRequest
-): Promise<ChainRepository> {
- const res = await authFetch(`${API_BASE}/api/v1/chains/${chainId}/repositories`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(req),
- });
- if (!res.ok) {
- const error = await res.json().catch(() => ({ message: res.statusText }));
- throw new Error(error.message || `Failed to add chain repository: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Delete a repository from a chain */
-export async function deleteChainRepository(
- chainId: string,
- repositoryId: string
-): Promise<{ deleted: boolean }> {
- const res = await authFetch(
- `${API_BASE}/api/v1/chains/${chainId}/repositories/${repositoryId}`,
- { method: "DELETE" }
- );
- if (!res.ok) {
- throw new Error(`Failed to delete chain repository: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Set a repository as primary for a chain */
-export async function setChainRepositoryPrimary(
- chainId: string,
- repositoryId: string
-): Promise<ChainRepository> {
- const res = await authFetch(
- `${API_BASE}/api/v1/chains/${chainId}/repositories/${repositoryId}/primary`,
- { method: "PUT" }
- );
- if (!res.ok) {
- throw new Error(`Failed to set chain repository as primary: ${res.statusText}`);
- }
- return res.json();
-}
-
-// =============================================================================
-// Directive Types and API
-// =============================================================================
-
-/** Directive status */
-export type DirectiveStatus =
- | "draft"
- | "planning"
- | "active"
- | "paused"
- | "completed"
- | "archived"
- | "failed";
-
-/** Autonomy level */
-export type AutonomyLevel = "full_auto" | "guardrails" | "manual";
-
-/** Confidence level (traffic light) */
-export type ConfidenceLevel = "green" | "yellow" | "red";
-
-/** Step status */
-export type StepStatus =
- | "pending"
- | "ready"
- | "running"
- | "evaluating"
- | "passed"
- | "failed"
- | "rework"
- | "skipped"
- | "blocked";
-
-/** Evaluation type */
-export type EvaluationType = "programmatic" | "llm" | "composite" | "manual";
-
-/** Directive summary for list view */
-export interface DirectiveSummary {
- id: string;
- title: string;
- goal: string;
- status: DirectiveStatus;
- autonomyLevel: AutonomyLevel;
- repositoryUrl: string | null;
- currentChainId: string | null;
- currentChainGeneration: number | null;
- totalSteps: number;
- completedSteps: number;
- failedSteps: number;
- currentConfidence: number | null;
- totalCostUsd: number;
- createdAt: string;
- updatedAt: string;
-}
-
-/** Full directive */
-export interface Directive {
- id: string;
- ownerId: string;
- title: string;
- goal: string;
- status: DirectiveStatus;
- autonomyLevel: AutonomyLevel;
- repositoryUrl: string | null;
- localPath: string | null;
- requirements: unknown | null;
- acceptanceCriteria: unknown | null;
- constraints: unknown | null;
- confidenceThresholdGreen: number;
- confidenceThresholdYellow: number;
- maxReworkCycles: number;
- maxTotalCostUsd: number | null;
- maxWallTimeMinutes: number | null;
- maxChainRegenerations: number;
- totalCostUsd: number;
- currentChainId: string | null;
- version: number;
- createdAt: string;
- updatedAt: string;
- startedAt: string | null;
- completedAt: string | null;
-}
-
-/** Directive chain */
-export interface DirectiveChain {
- id: string;
- directiveId: string;
- generation: number;
- name: string;
- description: string | null;
- rationale: string | null;
- planningModel: string | null;
- status: string;
- totalSteps: number;
- completedSteps: number;
- failedSteps: number;
- currentConfidence: number | null;
- startedAt: string | null;
- completedAt: string | null;
- version: number;
- createdAt: string;
- updatedAt: string;
-}
-
-/** Chain step */
-export interface ChainStep {
- id: string;
- chainId: string;
- name: string;
- description: string | null;
- stepType: string;
- contractType: string;
- initialPhase: string | null;
- taskPlan: string | null;
- phases: string[];
- dependsOn: string[];
- parallelGroup: string | null;
- requirementIds: string[];
- acceptanceCriteriaIds: string[];
- verifierConfig: unknown;
- status: StepStatus;
- contractId: string | null;
- supervisorTaskId: string | null;
- confidenceScore: number | null;
- confidenceLevel: ConfidenceLevel | null;
- evaluationCount: number;
- reworkCount: number;
- lastEvaluationId: string | null;
- editorX: number | null;
- editorY: number | null;
- startedAt: string | null;
- completedAt: string | null;
- createdAt: string;
- updatedAt: string;
-}
-
-/** Directive with progress info */
-export interface DirectiveWithProgress extends Directive {
- chain: DirectiveChain | null;
- steps: ChainStep[];
- recentEvents: DirectiveEvent[];
- pendingApprovals: DirectiveApproval[];
-}
-
-/** Directive evaluation */
-export interface DirectiveEvaluation {
- id: string;
- directiveId: string;
- chainId: string | null;
- stepId: string | null;
- evaluationType: EvaluationType;
- passed: boolean;
- overallScore: number;
- confidenceLevel: ConfidenceLevel;
- programmaticResults: unknown | null;
- llmResults: unknown | null;
- compositeBreakdown: unknown | null;
- feedback: string | null;
- reworkInstructions: string | null;
- verifierIds: string[];
- evaluatedBy: string | null;
- createdAt: string;
-}
-
-/** Directive event */
-export interface DirectiveEvent {
- id: string;
- directiveId: string;
- chainId: string | null;
- stepId: string | null;
- eventType: string;
- severity: string;
- eventData: unknown | null;
- actorType: string;
- actorId: string | null;
- createdAt: string;
-}
-
-/** Directive approval */
-export interface DirectiveApproval {
- id: string;
- directiveId: string;
- chainId: string | null;
- stepId: string | null;
- approvalType: string;
- description: string;
- context: unknown | null;
- urgency: string;
- status: string;
- requestedAt: string;
- resolvedAt: string | null;
- resolvedBy: string | null;
- response: string | null;
-}
-
-/** Directive verifier */
-export interface DirectiveVerifier {
- id: string;
- directiveId: string;
- name: string;
- verifierType: string;
- command: string | null;
- workingDirectory: string | null;
- timeoutSeconds: number;
- environment: unknown;
- autoDetect: boolean;
- detectFiles: string[];
- weight: number;
- required: boolean;
- enabled: boolean;
- lastRunAt: string | null;
- lastResult: unknown | null;
- createdAt: string;
- updatedAt: string;
-}
-
-/** Directive graph node */
-export interface DirectiveGraphNode {
- id: string;
- name: string;
- stepType: string;
- status: StepStatus;
- confidenceScore: number | null;
- confidenceLevel: ConfidenceLevel | null;
- contractId: string | null;
- editorX: number | null;
- editorY: number | null;
-}
-
-/** Directive graph edge */
-export interface DirectiveGraphEdge {
- source: string;
- target: string;
-}
-
-/** Directive graph response */
-export interface DirectiveGraphResponse {
- chainId: string;
- directiveId: string;
- nodes: DirectiveGraphNode[];
- edges: DirectiveGraphEdge[];
-}
-
-/** Create directive request */
-export interface CreateDirectiveRequest {
- goal: string;
- repositoryUrl?: string;
- localPath?: string;
- autonomyLevel?: AutonomyLevel;
- confidenceThresholdGreen?: number;
- confidenceThresholdYellow?: number;
- maxReworkCycles?: number;
- maxTotalCostUsd?: number;
- maxWallTimeMinutes?: number;
-}
-
-/** Update directive request */
-export interface UpdateDirectiveRequest {
- title?: string;
- goal?: string;
- requirements?: unknown;
- acceptanceCriteria?: unknown;
- constraints?: unknown;
- autonomyLevel?: AutonomyLevel;
- confidenceThresholdGreen?: number;
- confidenceThresholdYellow?: number;
- maxReworkCycles?: number;
- maxTotalCostUsd?: number;
- maxWallTimeMinutes?: number;
- version?: number;
-}
-
-/** Add step request */
-export interface AddStepRequest {
- name: string;
- description?: string;
- stepType?: string;
- contractType?: string;
- initialPhase?: string;
- taskPlan?: string;
- phases?: string[];
- dependsOn?: string[];
- parallelGroup?: string;
- requirementIds?: string[];
- acceptanceCriteriaIds?: string[];
- verifierConfig?: unknown;
- editorX?: number;
- editorY?: number;
-}
-
-/** Update step request */
-export interface UpdateStepRequest {
- name?: string;
- description?: string;
- initialPhase?: string;
- taskPlan?: string;
- phases?: string[];
- dependsOn?: string[];
- parallelGroup?: string;
- requirementIds?: string[];
- acceptanceCriteriaIds?: string[];
- verifierConfig?: unknown;
- editorX?: number;
- editorY?: number;
-}
-
-/** Create verifier request */
-export interface CreateVerifierRequest {
- name: string;
- verifierType: string;
- command?: string;
- workingDirectory?: string;
- timeoutSeconds?: number;
- weight?: number;
- required?: boolean;
- enabled?: boolean;
-}
-
-/** Update verifier request */
-export interface UpdateVerifierRequest {
- command?: string;
- weight?: number;
- required?: boolean;
- enabled?: boolean;
-}
-
-/** Approval action request */
-export interface ApprovalActionRequest {
- response?: string;
-}
-
-/** Start directive response */
-export interface StartDirectiveResponse {
- directiveId: string;
- chainId: string;
- chainGeneration: number;
- steps: ChainStep[];
- status: string;
-}
-
-/** Directive list response */
-export interface DirectiveListResponse {
- directives: DirectiveSummary[];
- total: number;
-}
-
-// =============================================================================
-// Directive API Functions
-// =============================================================================
-
-/** List directives */
-export async function listDirectives(
- status?: DirectiveStatus,
- limit = 50,
- offset = 0
-): Promise<DirectiveListResponse> {
- const params = new URLSearchParams();
- if (status) params.set("status", status);
- params.set("limit", String(limit));
- params.set("offset", String(offset));
-
- const res = await authFetch(`${API_BASE}/api/v1/directives?${params}`);
- if (!res.ok) {
- throw new Error(`Failed to list directives: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Get directive by ID */
-export async function getDirective(directiveId: string): Promise<DirectiveWithProgress> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}`);
- if (!res.ok) {
- throw new Error(`Failed to get directive: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Create a new directive */
-export async function createDirective(req: CreateDirectiveRequest): Promise<Directive> {
- const res = await authFetch(`${API_BASE}/api/v1/directives`, {
- method: "POST",
- body: JSON.stringify(req),
- });
- if (!res.ok) {
- const errorText = await res.text();
- throw new Error(`Failed to create directive: ${errorText || res.statusText}`);
- }
- return res.json();
-}
-
-/** Update directive */
-export async function updateDirective(
- directiveId: string,
- req: UpdateDirectiveRequest
-): Promise<Directive> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}`, {
- method: "PUT",
- body: JSON.stringify(req),
- });
- if (!res.ok) {
- throw new Error(`Failed to update directive: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Archive directive */
-export async function archiveDirective(directiveId: string): Promise<{ archived: boolean }> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}`, {
- method: "DELETE",
- });
- if (!res.ok) {
- throw new Error(`Failed to archive directive: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Start directive */
-export async function startDirective(directiveId: string): Promise<StartDirectiveResponse> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/start`, {
- method: "POST",
- });
- if (!res.ok) {
- throw new Error(`Failed to start directive: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Pause directive */
-export async function pauseDirective(directiveId: string): Promise<Directive> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/pause`, {
- method: "POST",
- });
- if (!res.ok) {
- throw new Error(`Failed to pause directive: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Resume directive */
-export async function resumeDirective(directiveId: string): Promise<Directive> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/resume`, {
- method: "POST",
- });
- if (!res.ok) {
- throw new Error(`Failed to resume directive: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Stop directive */
-export async function stopDirective(directiveId: string): Promise<Directive> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/stop`, {
- method: "POST",
- });
- if (!res.ok) {
- throw new Error(`Failed to stop directive: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Get directive chain */
-export async function getDirectiveChain(
- directiveId: string
-): Promise<{ chain: DirectiveChain | null; steps: ChainStep[] }> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/chain`);
- if (!res.ok) {
- throw new Error(`Failed to get directive chain: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Get directive chain graph */
-export async function getDirectiveGraph(directiveId: string): Promise<DirectiveGraphResponse> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/chain/graph`);
- if (!res.ok) {
- throw new Error(`Failed to get directive graph: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Replan directive chain */
-export async function replanDirectiveChain(directiveId: string): Promise<DirectiveChain> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/chain/replan`, {
- method: "POST",
- });
- if (!res.ok) {
- throw new Error(`Failed to replan directive chain: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Add step to directive chain */
-export async function addDirectiveStep(
- directiveId: string,
- req: AddStepRequest
-): Promise<ChainStep> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/chain/steps`, {
- method: "POST",
- body: JSON.stringify(req),
- });
- if (!res.ok) {
- throw new Error(`Failed to add step: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Get step details */
-export async function getDirectiveStep(
- directiveId: string,
- stepId: string
-): Promise<ChainStep> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/steps/${stepId}`);
- if (!res.ok) {
- throw new Error(`Failed to get step: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Update step */
-export async function updateDirectiveStep(
- directiveId: string,
- stepId: string,
- req: UpdateStepRequest
-): Promise<ChainStep> {
- const res = await authFetch(
- `${API_BASE}/api/v1/directives/${directiveId}/chain/steps/${stepId}`,
- {
- method: "PUT",
- body: JSON.stringify(req),
- }
- );
- if (!res.ok) {
- throw new Error(`Failed to update step: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Delete step */
-export async function deleteDirectiveStep(
- directiveId: string,
- stepId: string
-): Promise<{ deleted: boolean }> {
- const res = await authFetch(
- `${API_BASE}/api/v1/directives/${directiveId}/chain/steps/${stepId}`,
- {
- method: "DELETE",
- }
- );
- if (!res.ok) {
- throw new Error(`Failed to delete step: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Skip step */
-export async function skipDirectiveStep(
- directiveId: string,
- stepId: string
-): Promise<ChainStep> {
- const res = await authFetch(
- `${API_BASE}/api/v1/directives/${directiveId}/steps/${stepId}/skip`,
- {
- method: "POST",
- }
- );
- if (!res.ok) {
- throw new Error(`Failed to skip step: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** List directive evaluations */
-export async function listDirectiveEvaluations(
- directiveId: string,
- limit = 50
-): Promise<DirectiveEvaluation[]> {
- const res = await authFetch(
- `${API_BASE}/api/v1/directives/${directiveId}/evaluations?limit=${limit}`
- );
- if (!res.ok) {
- throw new Error(`Failed to list evaluations: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** List directive events */
-export async function listDirectiveEvents(
- directiveId: string,
- limit = 50
-): Promise<DirectiveEvent[]> {
- const res = await authFetch(
- `${API_BASE}/api/v1/directives/${directiveId}/events?limit=${limit}`
- );
- if (!res.ok) {
- throw new Error(`Failed to list events: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** List directive verifiers */
-export async function listDirectiveVerifiers(
- directiveId: string
-): Promise<DirectiveVerifier[]> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/verifiers`);
- if (!res.ok) {
- throw new Error(`Failed to list verifiers: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Add verifier */
-export async function addDirectiveVerifier(
- directiveId: string,
- req: CreateVerifierRequest
-): Promise<DirectiveVerifier> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/verifiers`, {
- method: "POST",
- body: JSON.stringify(req),
- });
- if (!res.ok) {
- throw new Error(`Failed to add verifier: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Update verifier */
-export async function updateDirectiveVerifier(
- directiveId: string,
- verifierId: string,
- req: UpdateVerifierRequest
-): Promise<DirectiveVerifier> {
- const res = await authFetch(
- `${API_BASE}/api/v1/directives/${directiveId}/verifiers/${verifierId}`,
- {
- method: "PUT",
- body: JSON.stringify(req),
- }
- );
- if (!res.ok) {
- throw new Error(`Failed to update verifier: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Auto-detect verifiers */
-export async function autoDetectVerifiers(
- directiveId: string
-): Promise<DirectiveVerifier[]> {
- const res = await authFetch(
- `${API_BASE}/api/v1/directives/${directiveId}/verifiers/auto-detect`,
- {
- method: "POST",
- }
- );
- if (!res.ok) {
- throw new Error(`Failed to auto-detect verifiers: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** List pending approvals */
-export async function listDirectiveApprovals(
- directiveId: string
-): Promise<DirectiveApproval[]> {
- const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/approvals`);
- if (!res.ok) {
- throw new Error(`Failed to list approvals: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Approve request */
-export async function approveDirectiveRequest(
- directiveId: string,
- approvalId: string,
- req?: ApprovalActionRequest
-): Promise<DirectiveApproval> {
- const res = await authFetch(
- `${API_BASE}/api/v1/directives/${directiveId}/approvals/${approvalId}/approve`,
- {
- method: "POST",
- body: JSON.stringify(req || {}),
- }
- );
- if (!res.ok) {
- throw new Error(`Failed to approve request: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Deny request */
-export async function denyDirectiveRequest(
- directiveId: string,
- approvalId: string,
- req?: ApprovalActionRequest
-): Promise<DirectiveApproval> {
- const res = await authFetch(
- `${API_BASE}/api/v1/directives/${directiveId}/approvals/${approvalId}/deny`,
- {
- method: "POST",
- body: JSON.stringify(req || {}),
- }
- );
- if (!res.ok) {
- throw new Error(`Failed to deny request: ${res.statusText}`);
- }
- return res.json();
-}
-
-/** Subscribe to directive events via SSE */
-export async function subscribeToDirectiveEvents(
- directiveId: string,
- onEvent: (event: DirectiveEvent) => void,
- onError?: (error: Error) => void
-): Promise<() => void> {
- // Get auth token for the request
- let authToken: string | null = null;
- if (supabase) {
- const { data: { session } } = await supabase.auth.getSession();
- if (session?.access_token) {
- authToken = session.access_token;
- }
- }
-
- // Build URL with auth token as query param (since EventSource doesn't support headers)
- const url = new URL(`${API_BASE}/api/v1/directives/${directiveId}/events/stream`);
- if (authToken) {
- url.searchParams.set("token", authToken);
- } else {
- const apiKey = getStoredApiKey();
- if (apiKey) {
- url.searchParams.set("api_key", apiKey);
- }
- }
-
- // Create EventSource connection
- const eventSource = new EventSource(url.toString());
-
- eventSource.onmessage = (e) => {
- try {
- const event = JSON.parse(e.data) as DirectiveEvent;
- onEvent(event);
- } catch (err) {
- console.error("Failed to parse SSE event:", err);
- }
- };
-
- eventSource.onerror = (_e) => {
- if (onError) {
- onError(new Error("SSE connection error"));
- }
- };
-
- // Return cleanup function
- return () => {
- eventSource.close();
- };
-}
diff --git a/makima/frontend/src/main.tsx b/makima/frontend/src/main.tsx
index c90d292..50fffe4 100644
--- a/makima/frontend/src/main.tsx
+++ b/makima/frontend/src/main.tsx
@@ -12,7 +12,6 @@ import HomePage from "./routes/_index";
import ListenPage from "./routes/listen";
import FilesPage from "./routes/files";
import ContractsPage from "./routes/contracts";
-import DirectivesPage from "./routes/directives";
import WorkflowPage from "./routes/workflow";
import MeshPage from "./routes/mesh";
import HistoryPage from "./routes/history";
@@ -73,22 +72,6 @@ createRoot(document.getElementById("root")!).render(
}
/>
<Route
- path="/directives"
- element={
- <ProtectedRoute>
- <DirectivesPage />
- </ProtectedRoute>
- }
- />
- <Route
- path="/directives/:id"
- element={
- <ProtectedRoute>
- <DirectivesPage />
- </ProtectedRoute>
- }
- />
- <Route
path="/contracts/:id/files/:fileId"
element={
<ProtectedRoute>
diff --git a/makima/frontend/src/routes/directives.tsx b/makima/frontend/src/routes/directives.tsx
deleted file mode 100644
index 90f0854..0000000
--- a/makima/frontend/src/routes/directives.tsx
+++ /dev/null
@@ -1,184 +0,0 @@
-import { useState, useCallback, useEffect } from "react";
-import { useParams, useNavigate } from "react-router";
-import { Masthead } from "../components/Masthead";
-import { useDirectives } from "../hooks/useDirectives";
-import { useDirectiveDetail } from "../hooks/useDirectiveDetail";
-import { useAuth } from "../contexts/AuthContext";
-import type {
- DirectiveSummary,
- CreateDirectiveRequest,
- AutonomyLevel,
-} from "../lib/api";
-import { DirectiveList } from "../components/directives/DirectiveList";
-import { DirectiveDetail } from "../components/directives/DirectiveDetail";
-import { CreateDirectiveModal } from "../components/directives/CreateDirectiveModal";
-
-export default function DirectivesPage() {
- const { isAuthenticated, isAuthConfigured, isLoading: authLoading } = useAuth();
- const navigate = useNavigate();
-
- // Redirect to login if not authenticated (when auth is configured)
- useEffect(() => {
- if (!authLoading && isAuthConfigured && !isAuthenticated) {
- navigate("/login");
- }
- }, [authLoading, isAuthConfigured, isAuthenticated, navigate]);
-
- // Show loading while checking auth
- if (authLoading) {
- return (
- <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]">
- <Masthead showNav />
- <main className="flex-1 flex items-center justify-center">
- <p className="text-[#7788aa] font-mono text-sm">Loading...</p>
- </main>
- </div>
- );
- }
-
- // Don't render if not authenticated (will redirect)
- if (isAuthConfigured && !isAuthenticated) {
- return null;
- }
-
- return <DirectivesPageContent />;
-}
-
-function DirectivesPageContent() {
- const { id } = useParams<{ id: string }>();
- const navigate = useNavigate();
- const {
- directives,
- loading,
- error,
- createNewDirective,
- archiveExistingDirective,
- } = useDirectives();
-
- const {
- directive: directiveDetail,
- graph: directiveGraph,
- loading: detailLoading,
- refresh: refreshDetail,
- start: handleStart,
- pause: handlePause,
- resume: handleResume,
- stop: handleStop,
- } = useDirectiveDetail(id);
-
- const [isCreating, setIsCreating] = useState(false);
-
- const handleSelect = useCallback(
- (directiveId: string) => {
- navigate(`/directives/${directiveId}`);
- },
- [navigate]
- );
-
- const handleBack = useCallback(() => {
- navigate("/directives");
- }, [navigate]);
-
- const handleCreate = useCallback(() => {
- setIsCreating(true);
- }, []);
-
- const handleCreateSubmit = useCallback(
- async (goal: string, repositoryUrl: string | undefined, autonomyLevel: AutonomyLevel) => {
- const data: CreateDirectiveRequest = {
- goal: goal.trim(),
- repositoryUrl: repositoryUrl?.trim() || undefined,
- autonomyLevel,
- };
-
- try {
- const result = await createNewDirective(data);
- if (result) {
- setIsCreating(false);
- navigate(`/directives/${result.id}`);
- }
- } catch (err) {
- console.error("Failed to create directive:", err);
- }
- },
- [createNewDirective, navigate]
- );
-
- const handleCreateCancel = useCallback(() => {
- setIsCreating(false);
- }, []);
-
- const handleArchive = useCallback(
- async (directive: DirectiveSummary) => {
- if (confirm(`Are you sure you want to archive this directive?`)) {
- const success = await archiveExistingDirective(directive.id);
- if (success && directive.id === id) {
- navigate("/directives");
- }
- }
- },
- [archiveExistingDirective, id, navigate]
- );
-
- return (
- <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]">
- <Masthead showNav />
- <main className="flex-1 flex flex-col p-4 pt-2 gap-4 overflow-hidden">
- {error && (
- <div className="p-3 bg-red-400/10 border border-red-400/30 text-red-400 font-mono text-sm">
- {error}
- </div>
- )}
-
- {/* Create directive modal */}
- {isCreating && (
- <CreateDirectiveModal
- onSubmit={handleCreateSubmit}
- onCancel={handleCreateCancel}
- />
- )}
-
- <div className="flex-1 grid grid-cols-[350px_1fr] gap-4 min-h-0">
- {/* Directive list */}
- <DirectiveList
- directives={directives}
- loading={loading}
- onSelect={handleSelect}
- onCreate={handleCreate}
- selectedId={id}
- onArchive={handleArchive}
- />
-
- {/* Directive detail or empty state */}
- {directiveDetail ? (
- <DirectiveDetail
- directive={directiveDetail}
- graph={directiveGraph}
- loading={detailLoading}
- onBack={handleBack}
- onRefresh={refreshDetail}
- onStart={handleStart}
- onPause={handlePause}
- onResume={handleResume}
- onStop={handleStop}
- />
- ) : (
- <div className="panel h-full flex items-center justify-center">
- <div className="text-center">
- <p className="font-mono text-sm text-[#555] mb-4">
- Select a directive or create a new one
- </p>
- <button
- onClick={handleCreate}
- className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors uppercase"
- >
- + New Directive
- </button>
- </div>
- </div>
- )}
- </div>
- </main>
- </div>
- );
-}
diff --git a/makima/frontend/tsconfig.tsbuildinfo b/makima/frontend/tsconfig.tsbuildinfo
index cbaa81f..0fbecaa 100644
--- a/makima/frontend/tsconfig.tsbuildinfo
+++ b/makima/frontend/tsconfig.tsbuildinfo
@@ -1 +1 @@
-{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/components/gridoverlay.tsx","./src/components/japanesehovertext.tsx","./src/components/logo.tsx","./src/components/masthead.tsx","./src/components/navstrip.tsx","./src/components/phaseconfirmationnotification.tsx","./src/components/protectedroute.tsx","./src/components/rewritelink.tsx","./src/components/simplemarkdown.tsx","./src/components/supervisorquestionnotification.tsx","./src/components/charts/chartrenderer.tsx","./src/components/contracts/commandmodepanel.tsx","./src/components/contracts/contractcliinput.tsx","./src/components/contracts/contractcontextmenu.tsx","./src/components/contracts/contractdetail.tsx","./src/components/contracts/contractlist.tsx","./src/components/contracts/phasebadge.tsx","./src/components/contracts/phaseconfirmationmodal.tsx","./src/components/contracts/phasedeliverablespanel.tsx","./src/components/contracts/phasehint.tsx","./src/components/contracts/phaseprogressbar.tsx","./src/components/contracts/quickactionbuttons.tsx","./src/components/contracts/repositorypanel.tsx","./src/components/contracts/taskderivationpreview.tsx","./src/components/directives/approvalstab.tsx","./src/components/directives/chaintab.tsx","./src/components/directives/createdirectivemodal.tsx","./src/components/directives/directivedetail.tsx","./src/components/directives/directivelist.tsx","./src/components/directives/directivelistitem.tsx","./src/components/directives/evaluationstab.tsx","./src/components/directives/eventstab.tsx","./src/components/directives/overviewtab.tsx","./src/components/directives/stepnode.tsx","./src/components/directives/verifierstab.tsx","./src/components/directives/index.ts","./src/components/files/bodyrenderer.tsx","./src/components/files/cliinput.tsx","./src/components/files/conflictnotification.tsx","./src/components/files/elementcontextmenu.tsx","./src/components/files/filedetail.tsx","./src/components/files/filelist.tsx","./src/components/files/reposyncindicator.tsx","./src/components/files/updatenotification.tsx","./src/components/files/versionhistorydropdown.tsx","./src/components/history/checkpointcard.tsx","./src/components/history/checkpointlist.tsx","./src/components/history/conversationmessage.tsx","./src/components/history/conversationview.tsx","./src/components/history/historyfilters.tsx","./src/components/history/resumecontrols.tsx","./src/components/history/timelineeventcard.tsx","./src/components/history/timelinelist.tsx","./src/components/history/index.ts","./src/components/listen/contractpickermodal.tsx","./src/components/listen/controlpanel.tsx","./src/components/listen/discusscontractmodal.tsx","./src/components/listen/speakerpanel.tsx","./src/components/listen/transcriptanalysispanel.tsx","./src/components/listen/transcriptpanel.tsx","./src/components/mesh/branchtaskmodal.tsx","./src/components/mesh/contractcompletequestion.tsx","./src/components/mesh/directoryinput.tsx","./src/components/mesh/gitactionspanel.tsx","./src/components/mesh/inlinesubtaskeditor.tsx","./src/components/mesh/mergeconflictresolver.tsx","./src/components/mesh/overlaydiffviewer.tsx","./src/components/mesh/prpreview.tsx","./src/components/mesh/patcheslistpanel.tsx","./src/components/mesh/subtasktree.tsx","./src/components/mesh/taskdetail.tsx","./src/components/mesh/tasklist.tsx","./src/components/mesh/taskoutput.tsx","./src/components/mesh/tasktree.tsx","./src/components/mesh/unifiedmeshchatinput.tsx","./src/components/mesh/worktreefilespanel.tsx","./src/components/workflow/phasecolumn.tsx","./src/components/workflow/workflowboard.tsx","./src/components/workflow/workflowcontractcard.tsx","./src/contexts/authcontext.tsx","./src/contexts/supervisorquestionscontext.tsx","./src/hooks/usecontracts.ts","./src/hooks/usedirectivedetail.ts","./src/hooks/usedirectives.ts","./src/hooks/usefilesubscription.ts","./src/hooks/usefiles.ts","./src/hooks/usemeshchathistory.ts","./src/hooks/usemicrophone.ts","./src/hooks/usespeakwebsocket.ts","./src/hooks/usetasksubscription.ts","./src/hooks/usetasks.ts","./src/hooks/usetextscramble.ts","./src/hooks/useversionhistory.ts","./src/hooks/usewebsocket.ts","./src/lib/api.ts","./src/lib/listenapi.ts","./src/lib/markdown.ts","./src/lib/supabase.ts","./src/routes/_index.tsx","./src/routes/contract-file.tsx","./src/routes/contracts.tsx","./src/routes/directives.tsx","./src/routes/files.tsx","./src/routes/history.tsx","./src/routes/listen.tsx","./src/routes/login.tsx","./src/routes/mesh.tsx","./src/routes/settings.tsx","./src/routes/speak.tsx","./src/routes/workflow.tsx","./src/types/messages.ts"],"version":"5.9.3"} \ No newline at end of file
+{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/components/gridoverlay.tsx","./src/components/japanesehovertext.tsx","./src/components/logo.tsx","./src/components/masthead.tsx","./src/components/navstrip.tsx","./src/components/phaseconfirmationnotification.tsx","./src/components/protectedroute.tsx","./src/components/rewritelink.tsx","./src/components/simplemarkdown.tsx","./src/components/supervisorquestionnotification.tsx","./src/components/charts/chartrenderer.tsx","./src/components/contracts/commandmodepanel.tsx","./src/components/contracts/contractcliinput.tsx","./src/components/contracts/contractcontextmenu.tsx","./src/components/contracts/contractdetail.tsx","./src/components/contracts/contractlist.tsx","./src/components/contracts/phasebadge.tsx","./src/components/contracts/phaseconfirmationmodal.tsx","./src/components/contracts/phasedeliverablespanel.tsx","./src/components/contracts/phasehint.tsx","./src/components/contracts/phaseprogressbar.tsx","./src/components/contracts/quickactionbuttons.tsx","./src/components/contracts/repositorypanel.tsx","./src/components/contracts/taskderivationpreview.tsx","./src/components/files/bodyrenderer.tsx","./src/components/files/cliinput.tsx","./src/components/files/conflictnotification.tsx","./src/components/files/elementcontextmenu.tsx","./src/components/files/filedetail.tsx","./src/components/files/filelist.tsx","./src/components/files/reposyncindicator.tsx","./src/components/files/updatenotification.tsx","./src/components/files/versionhistorydropdown.tsx","./src/components/history/checkpointcard.tsx","./src/components/history/checkpointlist.tsx","./src/components/history/conversationmessage.tsx","./src/components/history/conversationview.tsx","./src/components/history/historyfilters.tsx","./src/components/history/resumecontrols.tsx","./src/components/history/timelineeventcard.tsx","./src/components/history/timelinelist.tsx","./src/components/history/index.ts","./src/components/listen/contractpickermodal.tsx","./src/components/listen/controlpanel.tsx","./src/components/listen/discusscontractmodal.tsx","./src/components/listen/speakerpanel.tsx","./src/components/listen/transcriptanalysispanel.tsx","./src/components/listen/transcriptpanel.tsx","./src/components/mesh/branchtaskmodal.tsx","./src/components/mesh/contractcompletequestion.tsx","./src/components/mesh/directoryinput.tsx","./src/components/mesh/gitactionspanel.tsx","./src/components/mesh/inlinesubtaskeditor.tsx","./src/components/mesh/mergeconflictresolver.tsx","./src/components/mesh/overlaydiffviewer.tsx","./src/components/mesh/prpreview.tsx","./src/components/mesh/patcheslistpanel.tsx","./src/components/mesh/subtasktree.tsx","./src/components/mesh/taskdetail.tsx","./src/components/mesh/tasklist.tsx","./src/components/mesh/taskoutput.tsx","./src/components/mesh/tasktree.tsx","./src/components/mesh/unifiedmeshchatinput.tsx","./src/components/mesh/worktreefilespanel.tsx","./src/components/workflow/phasecolumn.tsx","./src/components/workflow/workflowboard.tsx","./src/components/workflow/workflowcontractcard.tsx","./src/contexts/authcontext.tsx","./src/contexts/supervisorquestionscontext.tsx","./src/hooks/usecontracts.ts","./src/hooks/usefilesubscription.ts","./src/hooks/usefiles.ts","./src/hooks/usemeshchathistory.ts","./src/hooks/usemicrophone.ts","./src/hooks/usespeakwebsocket.ts","./src/hooks/usetasksubscription.ts","./src/hooks/usetasks.ts","./src/hooks/usetextscramble.ts","./src/hooks/useversionhistory.ts","./src/hooks/usewebsocket.ts","./src/lib/api.ts","./src/lib/listenapi.ts","./src/lib/markdown.ts","./src/lib/supabase.ts","./src/routes/_index.tsx","./src/routes/contract-file.tsx","./src/routes/contracts.tsx","./src/routes/files.tsx","./src/routes/history.tsx","./src/routes/listen.tsx","./src/routes/login.tsx","./src/routes/mesh.tsx","./src/routes/settings.tsx","./src/routes/speak.tsx","./src/routes/workflow.tsx","./src/types/messages.ts"],"version":"5.9.3"} \ No newline at end of file