Forge
csharp3407750f
1using System.Text;
2using AgentForge.Data;
3using AgentForge.Models;
4using AgentForge.Plugin.Issue.Pages;
5using AgentForge.Plugin.Sdk;
6using AgentForge.Abstractions;
7using AgentForge.Plugin.View;
8using AgentForge.Services;
9using Microsoft.AspNetCore.Builder;
10using Microsoft.Extensions.DependencyInjection;
11
12namespace AgentForge.Plugin.Issue;
13
14public static class ForgeIssueViewEndpoints
15{
16 public static void Map(WebApplication app)
17 {
18 app.MapGet($"/view/repos/{ForgeRepoRouting.Pattern}/issues/{{number:int}}", RenderIssueView);
19 app.MapPost($"/view/repos/{ForgeRepoRouting.Pattern}/issues/{{number:int}}/comment", CommentOnIssueFromView);
20 }
21
22 private static IResult RenderIssueView(
23 string name,
24 int number,
25 ForgeRepository repository,
26 ForgeUrls urls,
27 GitRepoService git,
28 HttpContext http)
29 {
30 var repo = ForgePluginEndpoints.RequireRepo(repository, name, http);
31 var issue = repository.GetIssueByNumber(repo.Id, number)
32 ?? throw new ForgeApiException(404, $"Issue #{number} not found.");
33 var defaultBranch = git.BareRepoExists(repo.Name) ? git.GetDefaultBranch(repo.Name) : null;
34 var comments = repository.ListIssueComments(issue.Id)
35 .Select(c => (c.Author, c.Body, c.CreatedAt))
36 .ToList();
37
38 var iop = new StringBuilder();
39 ForgeIopViewRenderer.AppendIssuePanel(iop, name, issue, comments);
40 var body = ForgeIssuePage.Render(name, number, issue, defaultBranch, urls, comments, iop.ToString());
41 return ForgeViewPresenter.RepoPage($"Issue #{number}", repo, "issues", repository, body, http);
42 }
43
44 private static async Task<IResult> CommentOnIssueFromView(
45 string name,
46 int number,
47 HttpContext http,
48 ForgeRepository repository,
49 CiWebhookService ci,
50 CancellationToken cancellationToken)
51 {
52 var repo = ForgePluginEndpoints.RequireRepo(repository, name, http);
53 var body = http.Request.Form["body"].ToString().Trim();
54 if (body.Length == 0)
55 return Results.Redirect($"/view/repos/{Uri.EscapeDataString(name)}/issues/{number}");
56
57 var token = ForgeViewSession.TryGetToken(http, http.RequestServices.GetRequiredService<ForgeAuthService>());
58 var author = token is not null ? ForgeActorResolver.NormalizeAgentIdentity(token.Name) : "human";
59 var comment = repository.InsertIssueComment(repo.Id, number, body, author);
60 await ci.EmitIopAsync(
61 ForgeIopEvent.ForIssue("issue.comment", repo.Name, number, ForgePluginResponses.ToCommentResponse(comment), author),
62 cancellationToken).ConfigureAwait(false);
63 return Results.Redirect($"/view/repos/{Uri.EscapeDataString(name)}/issues/{number}");
64 }
65}
66
View only · write via MCP/CIDE