summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-26 03:09:36 +0000
committersoryu <soryu@soryu.co>2026-01-26 03:09:36 +0000
commit34d80fdead09e7deca364e8fad11b901abe8fbec (patch)
tree3d23e7975f94233e6406b855b16f080ef92afa60
parentcb4f2fc40dbabb40de948512eee74c7e46264665 (diff)
downloadsoryu-makima/task-task-fee9930d-fee9930d.tar.gz
soryu-makima/task-task-fee9930d-fee9930d.zip
[WIP] Heartbeat checkpoint - 2026-01-26 03:09:36 UTCmakima/task-task-fee9930d-fee9930d
-rw-r--r--makima/frontend/src/routes/mesh.tsx82
1 files changed, 80 insertions, 2 deletions
diff --git a/makima/frontend/src/routes/mesh.tsx b/makima/frontend/src/routes/mesh.tsx
index fb366a2..dfc6123 100644
--- a/makima/frontend/src/routes/mesh.tsx
+++ b/makima/frontend/src/routes/mesh.tsx
@@ -8,8 +8,8 @@ import { UnifiedMeshChatInput } from "../components/mesh/UnifiedMeshChatInput";
import { ContractCompleteQuestion } from "../components/mesh/ContractCompleteQuestion";
import { useTasks } from "../hooks/useTasks";
import { useTaskSubscription, type TaskUpdateEvent, type TaskOutputEvent } from "../hooks/useTaskSubscription";
-import type { TaskWithSubtasks, MeshChatContext, ContractSummary, ContractWithRelations, DaemonDirectory, TaskSummary, RepositoryHistoryEntry } from "../lib/api";
-import { startTask as startTaskApi, stopTask as stopTaskApi, getTaskOutput, listContracts, getContract, getDaemonDirectories, continueTask as continueTaskApi, resumeSupervisor, branchTask, getRepositorySuggestions } from "../lib/api";
+import type { TaskWithSubtasks, MeshChatContext, ContractSummary, ContractWithRelations, DaemonDirectory, TaskSummary, RepositoryHistoryEntry, ContractTypeTemplate } from "../lib/api";
+import { startTask as startTaskApi, stopTask as stopTaskApi, getTaskOutput, listContracts, getContract, getDaemonDirectories, continueTask as continueTaskApi, resumeSupervisor, branchTask, getRepositorySuggestions, listContractTypes } from "../lib/api";
import { DirectoryInput } from "../components/mesh/DirectoryInput";
import { useAuth } from "../contexts/AuthContext";
import { useSupervisorQuestions } from "../contexts/SupervisorQuestionsContext";
@@ -131,6 +131,10 @@ export default function MeshPage() {
const [standaloneRepoPath, setStandaloneRepoPath] = useState("");
const [repoSuggestions, setRepoSuggestions] = useState<RepositoryHistoryEntry[]>([]);
const [showRepoSuggestions, setShowRepoSuggestions] = useState(false);
+ // Contract type selection for standalone tasks
+ const [standaloneContractTypes, setStandaloneContractTypes] = useState<ContractTypeTemplate[]>([]);
+ const [standaloneContractType, setStandaloneContractType] = useState<string>("simple");
+ const [contractTypesLoading, setContractTypesLoading] = useState(false);
// Track which subtask's output we're viewing (null = parent task)
const [viewingSubtaskId, setViewingSubtaskId] = useState<string | null>(null);
const [viewingSubtaskName, setViewingSubtaskName] = useState<string | null>(null);
@@ -364,6 +368,41 @@ export default function MeshPage() {
}
}, [showContractModal, modalStep, selectedContract, standaloneRepoType]);
+ // Fetch contract types when standalone task modal is open
+ useEffect(() => {
+ if (showContractModal && modalStep === 2 && !selectedContract) {
+ setContractTypesLoading(true);
+ listContractTypes()
+ .then((res) => {
+ setStandaloneContractTypes(res.types);
+ setContractTypesLoading(false);
+ })
+ .catch((err) => {
+ console.error("Failed to fetch contract types:", err);
+ // Fall back to built-in types
+ setStandaloneContractTypes([
+ {
+ id: "simple",
+ name: "Simple",
+ description: "Plan → Execute: Simple workflow with a plan document",
+ phases: ["plan", "execute"],
+ defaultPhase: "plan",
+ isBuiltin: true,
+ },
+ {
+ id: "specification",
+ name: "Specification",
+ description: "Research → Specify → Plan → Execute → Review: Full specification-driven development with TDD",
+ phases: ["research", "specify", "plan", "execute", "review"],
+ defaultPhase: "research",
+ isBuiltin: true,
+ },
+ ]);
+ setContractTypesLoading(false);
+ });
+ }
+ }, [showContractModal, modalStep, selectedContract]);
+
// Apply a repository suggestion
const applyRepoSuggestion = useCallback((suggestion: RepositoryHistoryEntry) => {
if (suggestion.repositoryUrl) {
@@ -576,6 +615,7 @@ export default function MeshPage() {
setStandaloneRepoType("remote");
setStandaloneRepoUrl("");
setStandaloneRepoPath("");
+ setStandaloneContractType("simple");
setModalStep(2);
}, []);
@@ -625,6 +665,7 @@ export default function MeshPage() {
setStandaloneRepoType("remote");
setStandaloneRepoUrl("");
setStandaloneRepoPath("");
+ setStandaloneContractType("simple");
setRepoSuggestions([]);
setShowRepoSuggestions(false);
}, []);
@@ -1006,6 +1047,43 @@ export default function MeshPage() {
)}
</div>
+ {/* Contract type selector - for standalone tasks */}
+ {!selectedContract && (
+ <div className="space-y-1">
+ <label className="block text-[10px] font-mono uppercase tracking-wide text-[#7788aa]">
+ Contract Type
+ </label>
+ {contractTypesLoading ? (
+ <div className="flex items-center justify-center py-4">
+ <span className="font-mono text-xs text-[#8b949e]">Loading contract types...</span>
+ </div>
+ ) : (
+ <>
+ <div className="flex gap-2">
+ {standaloneContractTypes.map((type) => (
+ <button
+ key={type.id}
+ type="button"
+ onClick={() => setStandaloneContractType(type.id)}
+ className={`flex-1 px-3 py-2 font-mono text-xs uppercase transition-colors ${
+ standaloneContractType === type.id
+ ? "bg-[#0f3c78] text-[#dbe7ff] border border-[#75aafc]"
+ : "bg-[#0d1b2d] text-[#8b949e] border border-[#3f6fb3] hover:border-[#75aafc]"
+ }`}
+ >
+ {type.name}
+ </button>
+ ))}
+ </div>
+ <p className="mt-1 font-mono text-[10px] text-[#556677]">
+ {standaloneContractTypes.find((t) => t.id === standaloneContractType)?.description ||
+ "Select a contract type"}
+ </p>
+ </>
+ )}
+ </div>
+ )}
+
{/* Task name */}
<div className="space-y-1">
<label className="block text-[10px] font-mono uppercase tracking-wide text-[#7788aa]">Task Name</label>