package main import "github.com/orchard9/rdev/pkg/api" func registerAgentPaths(spec *api.OpenAPISpec) { spec.AddPath("/agents", "get", withAuth( "List code agents", `Returns all registered code agent providers and their status. Shows which agents are available, their supported models, and the current default.`, "Code Agents", "projects:read", `{ "agents": [ { "provider": "claudecode", "name": "Claude Code", "available": true, "default": true, "supported_models": ["claude-sonnet-4-20250514"], "default_model": "claude-sonnet-4-20250514" }, { "provider": "opencode", "name": "OpenCode", "available": false, "default": false, "supported_models": ["gpt-4o", "claude-sonnet-4-20250514"], "default_model": "claude-sonnet-4-20250514" } ], "default_agent": "claudecode", "total_agents": 2, "available_count": 1 }`, )) spec.AddPath("/agents/health", "get", withAuth( "Get agent health status", `Returns the health status of all registered code agents. Checks connectivity to each agent backend and reports availability.`, "Code Agents", "projects:read", `{ "agents": [ { "provider": "claudecode", "name": "Claude Code", "healthy": true, "message": "available", "latency": "125ms", "checked_at": "2026-01-27T12:00:00Z" }, { "provider": "opencode", "name": "OpenCode", "healthy": false, "message": "unavailable", "latency": "5.002s", "checked_at": "2026-01-27T12:00:00Z" } ], "healthy_count": 1, "total_count": 2, "default_agent": "claudecode", "default_healthy": true }`, )) spec.AddPath("/agents/{provider}", "get", withAuthAndParams( "Get agent capabilities", `Returns detailed capabilities for a specific code agent provider. Includes supported features, models, and configuration options.`, "Code Agents", "projects:read", []param{{Name: "provider", In: "path", Description: "Agent provider ID (e.g., 'claudecode', 'opencode')", Required: true}}, )) spec.AddPath("/agents/default", "post", withAuthAndBody( "Set default agent", `Changes the default code agent used for command execution. The specified provider must be registered and ideally available.`, "Code Agents", "admin", `{"provider": "opencode"}`, `{ "default_agent": "opencode", "message": "default agent updated" }`, )) } // param represents an OpenAPI parameter. type param struct { Name string In string Description string Required bool } // withAuth creates an operation that requires authentication. func withAuth(summary, description, tag, scope, example string) map[string]any { return map[string]any{ "summary": summary, "description": description + "\n\n**Required scope**: `" + scope + "`", "tags": []string{tag}, "security": []map[string]any{ {"ApiKeyAuth": []string{}}, }, "responses": map[string]any{ "200": map[string]any{ "description": "Success", "content": map[string]any{ "application/json": map[string]any{ "example": example, }, }, }, "401": map[string]any{"description": "Unauthorized - Missing or invalid API key"}, "403": map[string]any{"description": "Forbidden - Insufficient permissions"}, }, } } // withAuthAndBody creates an operation with auth and request body. func withAuthAndBody(summary, description, tag, scope, requestExample, responseExample string) map[string]any { return map[string]any{ "summary": summary, "description": description + "\n\n**Required scope**: `" + scope + "`", "tags": []string{tag}, "security": []map[string]any{ {"ApiKeyAuth": []string{}}, }, "requestBody": map[string]any{ "required": true, "content": map[string]any{ "application/json": map[string]any{ "example": requestExample, }, }, }, "responses": map[string]any{ "201": map[string]any{ "description": "Created", "content": map[string]any{ "application/json": map[string]any{ "example": responseExample, }, }, }, "400": map[string]any{"description": "Bad Request - Invalid input"}, "401": map[string]any{"description": "Unauthorized - Missing or invalid API key"}, "403": map[string]any{"description": "Forbidden - Insufficient permissions"}, }, } } // withAuthAndParams creates an operation with auth and path parameters. func withAuthAndParams(summary, description, tag, scope string, params []param) map[string]any { parameters := make([]map[string]any, len(params)) for i, p := range params { parameters[i] = map[string]any{ "name": p.Name, "in": p.In, "description": p.Description, "required": p.Required, "schema": map[string]any{"type": "string"}, } } return map[string]any{ "summary": summary, "description": description + "\n\n**Required scope**: `" + scope + "`", "tags": []string{tag}, "security": []map[string]any{ {"ApiKeyAuth": []string{}}, }, "parameters": parameters, "responses": map[string]any{ "200": map[string]any{"description": "Success"}, "401": map[string]any{"description": "Unauthorized - Missing or invalid API key"}, "403": map[string]any{"description": "Forbidden - Insufficient permissions"}, "404": map[string]any{"description": "Not Found"}, }, } } // withAuthBodyAndParams creates an operation with auth, body, and params. func withAuthBodyAndParams(summary, description, tag, scope string, params []param, requestExample, responseExample string) map[string]any { parameters := make([]map[string]any, len(params)) for i, p := range params { parameters[i] = map[string]any{ "name": p.Name, "in": p.In, "description": p.Description, "required": p.Required, "schema": map[string]any{"type": "string"}, } } return map[string]any{ "summary": summary, "description": description + "\n\n**Required scope**: `" + scope + "`", "tags": []string{tag}, "security": []map[string]any{ {"ApiKeyAuth": []string{}}, }, "parameters": parameters, "requestBody": map[string]any{ "required": true, "content": map[string]any{ "application/json": map[string]any{ "example": requestExample, }, }, }, "responses": map[string]any{ "201": map[string]any{ "description": "Created", "content": map[string]any{ "application/json": map[string]any{ "example": responseExample, }, }, }, "400": map[string]any{"description": "Bad Request - Invalid input"}, "401": map[string]any{"description": "Unauthorized - Missing or invalid API key"}, "403": map[string]any{"description": "Forbidden - Insufficient permissions"}, }, } } func registerWorkerPaths(spec *api.OpenAPISpec) { spec.AddPath("/workers", "get", withAuth( "List workers", "Returns all registered workers in the pool with status summary.", "Workers", "admin", `{ "workers": [ { "id": "rdev-worker-0", "hostname": "rdev-worker-0.rdev.svc", "status": "idle", "capabilities": ["build", "test", "deploy"], "registered_at": "2026-01-27T12:00:00Z", "last_heartbeat": "2026-01-27T12:05:00Z", "version": "1.0.0" } ], "total": 1, "summary": {"idle": 1, "busy": 0, "draining": 0, "offline": 0} }`, )) spec.AddPath("/workers/{workerId}", "get", withAuthAndParams( "Get worker", "Returns details for a specific worker.", "Workers", "admin", []param{{Name: "workerId", In: "path", Description: "Worker ID", Required: true}}, )) spec.AddPath("/workers/{workerId}/drain", "post", withAuthAndParams( "Drain worker", "Sets a worker to draining status. It will finish its current task but stop accepting new work.", "Workers", "admin", []param{{Name: "workerId", In: "path", Description: "Worker ID", Required: true}}, )) } func registerSDLCPaths(spec *api.OpenAPISpec) { // State and classifier spec.AddPath("/projects/{id}/sdlc/state", "get", withAuthAndParams( "Get SDLC state", "Returns the global SDLC state for a project.", "SDLC", "projects:read", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, )) spec.AddPath("/projects/{id}/sdlc/next", "get", map[string]any{ "summary": "Get next action", "description": "Returns the classifier's recommended next action.\n\n**Required scope**: `projects:read`", "tags": []string{"SDLC"}, "security": []map[string]any{{"ApiKeyAuth": []string{}}}, "parameters": []map[string]any{ {"name": "id", "in": "path", "description": "Project ID", "required": true, "schema": map[string]any{"type": "string"}}, {"name": "feature", "in": "query", "description": "Feature slug (optional)", "schema": map[string]any{"type": "string"}}, }, "responses": map[string]any{ "200": map[string]any{"description": "Success"}, "401": map[string]any{"description": "Unauthorized"}, }, }) // Features spec.AddPath("/projects/{id}/sdlc/features", "get", withAuthAndParams( "List features", "Returns all features in the project.", "SDLC", "projects:read", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, )) spec.AddPath("/projects/{id}/sdlc/features", "post", withAuthBodyAndParams( "Create feature", "Creates a new feature in the draft phase.", "SDLC", "projects:execute", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, `{"slug": "auth-flow", "title": "User Authentication Flow"}`, `{"slug": "auth-flow", "title": "User Authentication Flow", "phase": "draft"}`, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}", "get", withAuthAndParams( "Get feature", "Returns details for a specific feature.", "SDLC", "projects:read", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}", "delete", withAuthAndParams( "Delete feature", "Deletes a feature from the project.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/transition", "post", withAuthBodyAndParams( "Transition feature", "Moves a feature to a new phase.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, `{"phase": "specified"}`, `{"status": "transitioned", "phase": "specified"}`, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/block", "post", withAuthBodyAndParams( "Block feature", "Adds a blocker reason to a feature.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, `{"reason": "Waiting for API design"}`, `{"status": "blocked"}`, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/unblock", "post", withAuthAndParams( "Unblock feature", "Removes all blockers from a feature.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, )) // Artifacts spec.AddPath("/projects/{id}/sdlc/features/{slug}/artifacts", "get", withAuthAndParams( "Get artifact status", "Returns artifact statuses for a feature.", "SDLC", "projects:read", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/artifacts/{type}/approve", "post", withAuthAndParams( "Approve artifact", "Approves a feature artifact.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, {Name: "type", In: "path", Description: "Artifact type (spec, design, tasks, qa_plan, review, audit, qa_results)", Required: true}, }, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/artifacts/{type}/reject", "post", withAuthAndParams( "Reject artifact", "Rejects a feature artifact.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, {Name: "type", In: "path", Description: "Artifact type", Required: true}, }, )) // Tasks spec.AddPath("/projects/{id}/sdlc/features/{slug}/tasks", "get", withAuthAndParams( "List tasks", "Returns all tasks for a feature.", "SDLC", "projects:read", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/tasks", "post", withAuthBodyAndParams( "Add task", "Adds a new task to a feature.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, `{"title": "Implement login handler"}`, `{"id": "task-001", "title": "Implement login handler", "status": "pending"}`, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/tasks/{taskId}/start", "post", withAuthAndParams( "Start task", "Marks a task as in-progress.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, {Name: "taskId", In: "path", Description: "Task ID", Required: true}, }, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/tasks/{taskId}/complete", "post", withAuthAndParams( "Complete task", "Marks a task as complete.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, {Name: "taskId", In: "path", Description: "Task ID", Required: true}, }, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/tasks/{taskId}/block", "post", withAuthAndParams( "Block task", "Marks a task as blocked.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, {Name: "taskId", In: "path", Description: "Task ID", Required: true}, }, )) // Branch management spec.AddPath("/projects/{id}/sdlc/features/{slug}/branch", "post", withAuthAndParams( "Create branch", "Creates a feature branch and its manifest.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/branch", "get", withAuthAndParams( "Get branch status", "Returns branch status and merge checklist.", "SDLC", "projects:read", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/branch/sync", "post", withAuthAndParams( "Sync branch", "Syncs feature branch with base branch.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, )) // Merge and archive spec.AddPath("/projects/{id}/sdlc/features/{slug}/merge", "post", withAuthBodyAndParams( "Merge feature", "Merges a feature branch after all gates pass.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, `{"strategy": "squash"}`, `{"status": "merged", "strategy": "squash"}`, )) spec.AddPath("/projects/{id}/sdlc/features/{slug}/archive", "post", withAuthAndParams( "Archive feature", "Archives a released feature.", "SDLC", "projects:execute", []param{ {Name: "id", In: "path", Description: "Project ID", Required: true}, {Name: "slug", In: "path", Description: "Feature slug", Required: true}, }, )) // Query endpoints spec.AddPath("/projects/{id}/sdlc/query/blocked", "get", withAuthAndParams( "Query blocked features", "Returns all blocked features.", "SDLC", "projects:read", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, )) spec.AddPath("/projects/{id}/sdlc/query/ready", "get", withAuthAndParams( "Query ready features", "Returns features ready for work.", "SDLC", "projects:read", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, )) spec.AddPath("/projects/{id}/sdlc/query/needs-approval", "get", withAuthAndParams( "Query features needing approval", "Returns features awaiting approval.", "SDLC", "projects:read", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, )) // Orchestrator endpoints spec.AddPath("/projects/{id}/sdlc/execute", "post", withAuthBodyAndParams( "Execute action", "Executes the classifier's recommended next action.", "SDLC", "projects:execute", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, `{"feature": "auth-flow", "provider": "claude"}`, `{"action": "CREATE_SPEC", "success": true, "output": "..."}`, )) spec.AddPath("/projects/{id}/sdlc/resolve", "post", withAuthBodyAndParams( "Resolve blocker", "Resolves a feature blocker and re-classifies.", "SDLC", "projects:execute", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, `{"feature": "auth-flow"}`, `{"action": "TRANSITION", "success": true, "output": "Feature unblocked"}`, )) spec.AddPath("/projects/{id}/sdlc/commit", "post", withAuthBodyAndParams( "Commit changes", "Commits and optionally pushes changes in the project.", "SDLC", "projects:execute", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, `{"feature": "auth-flow", "message": "feat: implement login", "push": true}`, `{"commit_sha": "abc123", "files_changed": ["handler.go"], "pushed": true}`, )) } func registerBuildPaths(spec *api.OpenAPISpec) { spec.AddPath("/projects/{id}/builds", "post", withAuthBodyAndParams( "Start build", "Enqueues a build task for a project. The build will be picked up by an available worker.", "Builds", "projects:execute", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, `{ "prompt": "Build a landing page with Next.js and Tailwind CSS", "template": "nextjs-landing", "auto_commit": true, "auto_push": true, "callback_url": "https://example.com/webhook" }`, `{ "task_id": "task-abc123", "project_id": "my-project", "status": "pending", "status_url": "/builds/task-abc123" }`, )) spec.AddPath("/projects/{id}/builds", "get", withAuthAndParams( "List builds", "Returns build history for a project.", "Builds", "projects:read", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, )) spec.AddPath("/builds/{taskId}", "get", withAuthAndParams( "Get build status", "Returns the status and result of a specific build.", "Builds", "projects:read", []param{{Name: "taskId", In: "path", Description: "Build task ID", Required: true}}, )) spec.AddPath("/project/create-and-build", "post", withAuthAndBody( "Create project and build", `Creates a new project and immediately enqueues a build task. Combines project creation (git repo, DNS, CI activation) with build submission in a single call.`, "Builds", "admin", `{ "name": "my-landing-page", "description": "Landing page for product launch", "template": "nextjs-landing", "prompt": "Build a modern landing page with hero, features, and CTA sections", "auto_commit": true, "auto_push": true }`, `{ "project_id": "my-landing-page", "name": "my-landing-page", "domain": "my-landing-page.threesix.ai", "url": "https://my-landing-page.threesix.ai", "git": { "owner": "jordan", "name": "my-landing-page", "clone_http": "https://git.threesix.ai/jordan/my-landing-page.git" }, "task_id": "task-abc123", "status": "pending", "status_url": "/builds/task-abc123" }`, )) }