diff options
Diffstat (limited to 'makima/frontend/src/lib')
| -rw-r--r-- | makima/frontend/src/lib/api.ts | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts index b3c18a5..7c9fcd6 100644 --- a/makima/frontend/src/lib/api.ts +++ b/makima/frontend/src/lib/api.ts @@ -811,6 +811,112 @@ export async function retryCompletionAction( return res.json(); } +// ============================================================================= +// Git Actions for Tasks (Manual) +// ============================================================================= + +/** Response from export patch */ +export interface ExportPatchResponse { + success: boolean; + taskId: string; + fileName: string; + filePath?: string; + patchSize?: number; + message: string; +} + +/** + * Export a task's changes as a patch file. + * The patch will be saved to the contract's patch directory. + */ +export async function exportTaskPatch( + taskId: string, + fileName?: string +): Promise<ExportPatchResponse> { + const body: Record<string, unknown> = {}; + if (fileName) { + body.fileName = fileName; + } + const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/export-patch`, { + method: "POST", + body: JSON.stringify(body), + }); + if (!res.ok) { + const errorText = await res.text(); + throw new Error(`Failed to export patch: ${errorText || res.statusText}`); + } + return res.json(); +} + +/** Response from push branch */ +export interface PushBranchResponse { + success: boolean; + taskId: string; + branchName: string; + remote?: string; + message: string; +} + +/** + * Push a task's changes to a remote branch. + * Creates a branch if it doesn't exist and pushes the commits. + */ +export async function pushTaskBranch( + taskId: string, + branchName?: string +): Promise<PushBranchResponse> { + const body: Record<string, unknown> = {}; + if (branchName) { + body.branchName = branchName; + } + const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/push-branch`, { + method: "POST", + body: JSON.stringify(body), + }); + if (!res.ok) { + const errorText = await res.text(); + throw new Error(`Failed to push branch: ${errorText || res.statusText}`); + } + return res.json(); +} + +/** Response from create PR */ +export interface CreatePRResponse { + success: boolean; + taskId: string; + prUrl?: string; + prNumber?: number; + branchName?: string; + message: string; +} + +/** + * Create a pull request for a task's changes. + * First pushes the branch if needed, then creates the PR. + */ +export async function createTaskPR( + taskId: string, + title?: string, + body?: string +): Promise<CreatePRResponse> { + const reqBody: Record<string, unknown> = {}; + if (title) { + reqBody.title = title; + } + if (body) { + reqBody.body = body; + } + const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/create-pr`, { + method: "POST", + body: JSON.stringify(reqBody), + }); + if (!res.ok) { + const errorText = await res.text(); + throw new Error(`Failed to create PR: ${errorText || res.statusText}`); + } + return res.json(); +} + /** A suggested directory from a connected daemon */ export interface DaemonDirectory { /** Path to the directory */ @@ -1567,6 +1673,8 @@ export interface ContractSummary { status: ContractStatus; /** Supervisor task ID for contract orchestration */ supervisorTaskId: string | null; + /** When true, tasks won't auto-push or create PRs - use patch files instead */ + localOnly: boolean; fileCount: number; taskCount: number; repositoryCount: number; @@ -1589,6 +1697,8 @@ export interface Contract { autonomousLoop: boolean; /** Whether to wait for user confirmation before progressing to the next phase */ phaseGuard: boolean; + /** When true, tasks won't auto-push or create PRs - use patch files instead */ + localOnly: boolean; version: number; createdAt: string; updatedAt: string; @@ -1622,6 +1732,8 @@ export interface CreateContractRequest { contractType?: ContractType; /** Initial phase to start in (defaults based on contract type) */ initialPhase?: ContractPhase; + /** When true, tasks won't auto-push or create PRs - use patch files instead */ + localOnly?: boolean; } export interface UpdateContractRequest { @@ -2710,3 +2822,106 @@ export function getSupervisorStatus( export async function dismissTask(taskId: string): Promise<Task> { return updateTask(taskId, { hidden: true }); } + +// ============================================================================= +// Worktree Info Types and Functions +// ============================================================================= + +/** File status in the worktree (git status) */ +export type FileStatus = "M" | "A" | "D" | "R" | "C" | "U" | "?" | "modified" | "added" | "deleted" | "renamed" | "copied" | "unmerged" | "untracked"; + +/** A single changed file in the worktree */ +export interface WorktreeFile { + /** File path relative to worktree root */ + path: string; + /** Git status code (M=modified, A=added, D=deleted, R=renamed, C=copied, U=unmerged, ?=untracked) */ + status: FileStatus; + /** Lines added (0 if deleted or unavailable) */ + linesAdded: number; + /** Lines removed (0 if added or unavailable) */ + linesRemoved: number; +} + +/** Statistics about worktree changes */ +export interface WorktreeStats { + /** Number of files changed */ + filesChanged: number; + /** Total lines inserted */ + insertions: number; + /** Total lines deleted */ + deletions: number; +} + +/** Worktree information for a task */ +export interface WorktreeInfo { + /** Task ID */ + taskId: string; + /** Path to the worktree directory */ + worktreePath: string | null; + /** Whether the worktree exists on the daemon */ + exists: boolean; + /** Aggregate statistics */ + stats: WorktreeStats; + /** Changed files list */ + files: WorktreeFile[]; + /** Current branch name */ + branch: string | null; + /** Current HEAD commit SHA */ + headSha: string | null; +} + +/** + * Get worktree information for a task. + * Returns changed files, stats, and metadata about the worktree. + */ +export async function getWorktreeInfo(taskId: string): Promise<WorktreeInfo> { + const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/worktree-info`); + if (!res.ok) { + const errorText = await res.text(); + throw new Error(`Failed to get worktree info: ${errorText || res.statusText}`); + } + return res.json(); +} + +// ============================================================================= +// Patch Types and Functions +// ============================================================================= + +/** Summary of a patch file (contract file of type "patch") */ +export interface PatchSummary { + /** Patch/file ID */ + id: string; + /** Patch name */ + name: string; + /** Optional description */ + description: string | null; + /** Task ID this patch was created from */ + taskId: string | null; + /** Contract ID */ + contractId: string; + /** Number of files in the patch */ + filesCount: number; + /** Total lines added */ + linesAdded: number; + /** Total lines removed */ + linesRemoved: number; + /** List of file paths in the patch (if available) */ + files: string[] | null; + /** When the patch was created */ + createdAt: string; + /** When the patch was last updated */ + updatedAt: string; +} + +/** + * List patches for a task. + * Returns contract files of type "patch" associated with the task. + */ +export async function listTaskPatches(taskId: string, contractId: string): Promise<PatchSummary[]> { + const res = await authFetch(`${API_BASE}/api/v1/mesh/tasks/${taskId}/patches?contractId=${contractId}`); + if (!res.ok) { + const errorText = await res.text(); + throw new Error(`Failed to list patches: ${errorText || res.statusText}`); + } + return res.json(); +} |
