2026-03-16 11:10:33 +00:00
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
|
|
|
|
|
|
package agentic
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
|
2026-03-22 03:41:07 +00:00
|
|
|
core "dappco.re/go/core"
|
2026-03-22 14:15:41 +00:00
|
|
|
forge_types "dappco.re/go/core/forge/types"
|
2026-03-16 11:10:33 +00:00
|
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// --- agentic_create_pr ---
|
|
|
|
|
|
|
|
|
|
// CreatePRInput is the input for agentic_create_pr.
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
//
|
2026-03-29 21:56:45 +00:00
|
|
|
// input := agentic.CreatePRInput{Workspace: "core/go-io/task-42", Title: "Fix watcher panic"}
|
2026-03-16 11:10:33 +00:00
|
|
|
type CreatePRInput struct {
|
2026-03-29 21:56:45 +00:00
|
|
|
Workspace string `json:"workspace"` // workspace name (e.g. "core/go-io/task-42")
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
Title string `json:"title,omitempty"` // PR title (default: task description)
|
|
|
|
|
Body string `json:"body,omitempty"` // PR body (default: auto-generated)
|
|
|
|
|
Base string `json:"base,omitempty"` // base branch (default: "main")
|
|
|
|
|
DryRun bool `json:"dry_run,omitempty"` // preview without creating
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CreatePROutput is the output for agentic_create_pr.
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
//
|
|
|
|
|
// out := agentic.CreatePROutput{Success: true, PRURL: "https://forge.example/core/go-io/pulls/12", PRNum: 12}
|
2026-03-16 11:10:33 +00:00
|
|
|
type CreatePROutput struct {
|
|
|
|
|
Success bool `json:"success"`
|
|
|
|
|
PRURL string `json:"pr_url,omitempty"`
|
|
|
|
|
PRNum int `json:"pr_number,omitempty"`
|
|
|
|
|
Title string `json:"title"`
|
|
|
|
|
Branch string `json:"branch"`
|
|
|
|
|
Repo string `json:"repo"`
|
|
|
|
|
Pushed bool `json:"pushed"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *PrepSubsystem) registerCreatePRTool(server *mcp.Server) {
|
|
|
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
|
|
|
Name: "agentic_create_pr",
|
|
|
|
|
Description: "Create a pull request from an agent workspace. Pushes the branch to Forge and opens a PR. Links to the source issue if one was tracked.",
|
|
|
|
|
}, s.createPR)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *PrepSubsystem) createPR(ctx context.Context, _ *mcp.CallToolRequest, input CreatePRInput) (*mcp.CallToolResult, CreatePROutput, error) {
|
|
|
|
|
if input.Workspace == "" {
|
2026-03-22 03:41:07 +00:00
|
|
|
return nil, CreatePROutput{}, core.E("createPR", "workspace is required", nil)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
if s.forgeToken == "" {
|
2026-03-22 03:41:07 +00:00
|
|
|
return nil, CreatePROutput{}, core.E("createPR", "no Forge token configured", nil)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
wsDir := core.JoinPath(WorkspaceRoot(), input.Workspace)
|
2026-03-29 21:11:46 +00:00
|
|
|
repoDir := WorkspaceRepoDir(wsDir)
|
2026-03-16 11:10:33 +00:00
|
|
|
|
refactor(agentic): workspace = clone, prompt replaces files
Major simplification of the dispatch model:
- Workspace dir: .core/workspace/{org}/{repo}/{pr|task|branch|tag}/
- Clone into repo/ (not src/), metadata in .meta/
- One of issue, pr, branch, or tag required for dispatch
- All context (brain, consumers, git log, wiki, plan) assembled
into prompt string — no TODO.md, PROMPT.md, CONTEXT.md files
- Resume detection: skip clone if repo/.git exists
- Default agent changed to codex
- spawnAgent drops srcDir param, runs from repo/
- No --skip-git-repo-check (repo/ IS a git repo)
- All downstream files: srcDir → repoDir
Track PRs, not workspace iterations.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 13:41:59 +00:00
|
|
|
if !fs.IsDir(core.JoinPath(repoDir, ".git")) {
|
2026-03-26 06:38:02 +00:00
|
|
|
return nil, CreatePROutput{}, core.E("createPR", core.Concat("workspace not found: ", input.Workspace), nil)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read workspace status for repo, branch, issue context
|
2026-03-24 13:02:41 +00:00
|
|
|
st, err := ReadStatus(wsDir)
|
2026-03-16 11:10:33 +00:00
|
|
|
if err != nil {
|
2026-03-22 03:41:07 +00:00
|
|
|
return nil, CreatePROutput{}, core.E("createPR", "no status.json", err)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if st.Branch == "" {
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
branch := s.gitOutput(ctx, repoDir, "rev-parse", "--abbrev-ref", "HEAD")
|
2026-03-25 09:51:57 +00:00
|
|
|
if branch == "" {
|
|
|
|
|
return nil, CreatePROutput{}, core.E("createPR", "failed to detect branch", nil)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
2026-03-25 09:51:57 +00:00
|
|
|
st.Branch = branch
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
org := st.Org
|
|
|
|
|
if org == "" {
|
|
|
|
|
org = "core"
|
|
|
|
|
}
|
|
|
|
|
base := input.Base
|
|
|
|
|
if base == "" {
|
2026-03-23 12:53:33 +00:00
|
|
|
base = "dev"
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build PR title
|
|
|
|
|
title := input.Title
|
|
|
|
|
if title == "" {
|
|
|
|
|
title = st.Task
|
|
|
|
|
}
|
|
|
|
|
if title == "" {
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
title = core.Sprintf("Agent work on %s", st.Branch)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build PR body
|
|
|
|
|
body := input.Body
|
|
|
|
|
if body == "" {
|
|
|
|
|
body = s.buildPRBody(st)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if input.DryRun {
|
|
|
|
|
return nil, CreatePROutput{
|
|
|
|
|
Success: true,
|
|
|
|
|
Title: title,
|
|
|
|
|
Branch: st.Branch,
|
|
|
|
|
Repo: st.Repo,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 16:53:55 +00:00
|
|
|
// Push branch to Forge (origin is the local clone, not Forge)
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
forgeRemote := core.Sprintf("ssh://git@forge.lthn.ai:2223/%s/%s.git", org, st.Repo)
|
feat(v0.8.0): full AX migration — ServiceRuntime, Actions, quality gates, transport
go-process:
- Register factory, Result lifecycle, 5 named Action handlers
- Start/Run/StartWithOptions/RunWithOptions all return core.Result
- core.ID() replaces fmt.Sprintf, core.As replaces errors.As
core/agent:
- PrepSubsystem + monitor.Subsystem + setup.Service embed ServiceRuntime[T]
- 22 named Actions + agent.completion Task pipeline in OnStartup
- ChannelNotifier removed — all IPC via c.ACTION(messages.X{})
- proc.go: all methods via s.Core().Process(), returns core.Result
- status.go: WriteAtomic + JSONMarshalString
- paths.go: Fs.NewUnrestricted() replaces unsafe.Pointer
- transport.go: ONE net/http file — HTTPGet/HTTPPost/HTTPDo/MCP transport
- All disallowed imports eliminated from source files (13 quality gates)
- String concat eliminated — core.Concat() throughout
- 1:1 _test.go + _example_test.go for every source file
- Reference docs synced from core/go v0.8.0
- RFC-025 updated with net/http, net/url, io/fs quality gates
- lib.go: io/fs eliminated via Data.ListNames, Array[T].Deduplicate
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-26 01:27:46 +00:00
|
|
|
r := s.gitCmd(ctx, repoDir, "push", forgeRemote, st.Branch)
|
|
|
|
|
if !r.OK {
|
2026-03-26 06:38:02 +00:00
|
|
|
return nil, CreatePROutput{}, core.E("createPR", core.Concat("git push failed: ", r.Value.(string)), nil)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create PR via Forge API
|
|
|
|
|
prURL, prNum, err := s.forgeCreatePR(ctx, org, st.Repo, st.Branch, base, title, body)
|
|
|
|
|
if err != nil {
|
2026-03-22 03:41:07 +00:00
|
|
|
return nil, CreatePROutput{}, core.E("createPR", "failed to create PR", err)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update status with PR URL
|
|
|
|
|
st.PRURL = prURL
|
|
|
|
|
writeStatus(wsDir, st)
|
|
|
|
|
|
|
|
|
|
// Comment on issue if tracked
|
|
|
|
|
if st.Issue > 0 {
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
comment := core.Sprintf("Pull request created: %s", prURL)
|
2026-03-16 11:10:33 +00:00
|
|
|
s.commentOnIssue(ctx, org, st.Repo, st.Issue, comment)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, CreatePROutput{
|
|
|
|
|
Success: true,
|
|
|
|
|
PRURL: prURL,
|
|
|
|
|
PRNum: prNum,
|
|
|
|
|
Title: title,
|
|
|
|
|
Branch: st.Branch,
|
|
|
|
|
Repo: st.Repo,
|
|
|
|
|
Pushed: true,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *PrepSubsystem) buildPRBody(st *WorkspaceStatus) string {
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
b := core.NewBuilder()
|
2026-03-16 11:10:33 +00:00
|
|
|
b.WriteString("## Summary\n\n")
|
|
|
|
|
if st.Task != "" {
|
|
|
|
|
b.WriteString(st.Task)
|
|
|
|
|
b.WriteString("\n\n")
|
|
|
|
|
}
|
|
|
|
|
if st.Issue > 0 {
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
b.WriteString(core.Sprintf("Closes #%d\n\n", st.Issue))
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
b.WriteString(core.Sprintf("**Agent:** %s\n", st.Agent))
|
|
|
|
|
b.WriteString(core.Sprintf("**Runs:** %d\n", st.Runs))
|
2026-03-16 11:10:33 +00:00
|
|
|
b.WriteString("\n---\n*Created by agentic dispatch*\n")
|
|
|
|
|
return b.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *PrepSubsystem) forgeCreatePR(ctx context.Context, org, repo, head, base, title, body string) (string, int, error) {
|
2026-03-29 20:15:58 +00:00
|
|
|
var pr pullRequestView
|
|
|
|
|
err := s.forge.Client().Post(ctx, core.Sprintf("/api/v1/repos/%s/%s/pulls", org, repo), &forge_types.CreatePullRequestOption{
|
2026-03-22 14:15:41 +00:00
|
|
|
Title: title,
|
|
|
|
|
Body: body,
|
|
|
|
|
Head: head,
|
|
|
|
|
Base: base,
|
2026-03-29 20:15:58 +00:00
|
|
|
}, &pr)
|
2026-03-16 11:10:33 +00:00
|
|
|
if err != nil {
|
2026-03-22 14:15:41 +00:00
|
|
|
return "", 0, core.E("forgeCreatePR", "create PR failed", err)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
2026-03-29 20:15:58 +00:00
|
|
|
return pr.HTMLURL, int(pullRequestNumber(pr)), nil
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *PrepSubsystem) commentOnIssue(ctx context.Context, org, repo string, issue int, comment string) {
|
2026-03-22 14:15:41 +00:00
|
|
|
s.forge.Issues.CreateComment(ctx, org, repo, int64(issue), comment)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- agentic_list_prs ---
|
|
|
|
|
|
|
|
|
|
// ListPRsInput is the input for agentic_list_prs.
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
//
|
|
|
|
|
// input := agentic.ListPRsInput{Org: "core", Repo: "go-io", State: "open", Limit: 10}
|
2026-03-16 11:10:33 +00:00
|
|
|
type ListPRsInput struct {
|
|
|
|
|
Org string `json:"org,omitempty"` // forge org (default "core")
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
Repo string `json:"repo,omitempty"` // specific repo, or empty for all
|
2026-03-16 11:10:33 +00:00
|
|
|
State string `json:"state,omitempty"` // "open" (default), "closed", "all"
|
|
|
|
|
Limit int `json:"limit,omitempty"` // max results (default 20)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ListPRsOutput is the output for agentic_list_prs.
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
//
|
|
|
|
|
// out := agentic.ListPRsOutput{Success: true, Count: 2, PRs: []agentic.PRInfo{{Repo: "go-io", Number: 12}}}
|
2026-03-16 11:10:33 +00:00
|
|
|
type ListPRsOutput struct {
|
|
|
|
|
Success bool `json:"success"`
|
|
|
|
|
Count int `json:"count"`
|
|
|
|
|
PRs []PRInfo `json:"prs"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PRInfo represents a pull request.
|
refactor: migrate core/agent to Core primitives — reference implementation
Phase 1: go-io/go-log → core.Fs{}, core.E(), core.Error/Info/Warn
Phase 2: strings/fmt → core.Contains, core.Sprintf, core.Split etc
Phase 3: embed.FS → core.Mount/core.Embed, core.Extract
Phase 4: cmd/main.go → core.Command(), c.Cli().Run(), no cli package
All packages migrated:
- pkg/lib (Codex): core.Mount, core.Extract, Result returns, AX comments
- pkg/setup (Codex): core.Fs, core.E, fixed missing lib helpers
- pkg/brain (Codex): Core primitives, AX comments
- pkg/monitor (Codex): Core string/logging primitives
- pkg/agentic (Codex): 20 files, Core primitives throughout
- cmd/main.go: pure Core CLI, no fmt/log/filepath/strings/cli
Remaining stdlib: path/filepath (Core doesn't wrap OS paths),
fmt.Sscanf/strings.Map (no Core equivalent).
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-22 06:13:41 +00:00
|
|
|
//
|
|
|
|
|
// pr := agentic.PRInfo{Repo: "go-io", Number: 12, Title: "Migrate pkg/fs", Branch: "agent/migrate-fs"}
|
2026-03-16 11:10:33 +00:00
|
|
|
type PRInfo struct {
|
|
|
|
|
Repo string `json:"repo"`
|
|
|
|
|
Number int `json:"number"`
|
|
|
|
|
Title string `json:"title"`
|
|
|
|
|
State string `json:"state"`
|
|
|
|
|
Author string `json:"author"`
|
|
|
|
|
Branch string `json:"branch"`
|
|
|
|
|
Base string `json:"base"`
|
|
|
|
|
Labels []string `json:"labels,omitempty"`
|
|
|
|
|
Mergeable bool `json:"mergeable"`
|
|
|
|
|
URL string `json:"url"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *PrepSubsystem) registerListPRsTool(server *mcp.Server) {
|
|
|
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
|
|
|
Name: "agentic_list_prs",
|
|
|
|
|
Description: "List pull requests across Forge repos. Filter by org, repo, and state (open/closed/all).",
|
|
|
|
|
}, s.listPRs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *PrepSubsystem) listPRs(ctx context.Context, _ *mcp.CallToolRequest, input ListPRsInput) (*mcp.CallToolResult, ListPRsOutput, error) {
|
|
|
|
|
if s.forgeToken == "" {
|
2026-03-22 03:41:07 +00:00
|
|
|
return nil, ListPRsOutput{}, core.E("listPRs", "no Forge token configured", nil)
|
2026-03-16 11:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if input.Org == "" {
|
|
|
|
|
input.Org = "core"
|
|
|
|
|
}
|
|
|
|
|
if input.State == "" {
|
|
|
|
|
input.State = "open"
|
|
|
|
|
}
|
|
|
|
|
if input.Limit == 0 {
|
|
|
|
|
input.Limit = 20
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var repos []string
|
|
|
|
|
if input.Repo != "" {
|
|
|
|
|
repos = []string{input.Repo}
|
|
|
|
|
} else {
|
|
|
|
|
var err error
|
|
|
|
|
repos, err = s.listOrgRepos(ctx, input.Org)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, ListPRsOutput{}, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var allPRs []PRInfo
|
|
|
|
|
|
|
|
|
|
for _, repo := range repos {
|
|
|
|
|
prs, err := s.listRepoPRs(ctx, input.Org, repo, input.State)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
allPRs = append(allPRs, prs...)
|
|
|
|
|
|
|
|
|
|
if len(allPRs) >= input.Limit {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(allPRs) > input.Limit {
|
|
|
|
|
allPRs = allPRs[:input.Limit]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, ListPRsOutput{
|
|
|
|
|
Success: true,
|
|
|
|
|
Count: len(allPRs),
|
|
|
|
|
PRs: allPRs,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *PrepSubsystem) listRepoPRs(ctx context.Context, org, repo, state string) ([]PRInfo, error) {
|
2026-03-29 20:15:58 +00:00
|
|
|
var prs []pullRequestView
|
|
|
|
|
err := s.forge.Client().Get(ctx, core.Sprintf("/api/v1/repos/%s/%s/pulls?limit=50&page=1", org, repo), &prs)
|
2026-03-17 19:27:44 +00:00
|
|
|
if err != nil {
|
2026-03-26 06:38:02 +00:00
|
|
|
return nil, core.E("listRepoPRs", core.Concat("failed to list PRs for ", repo), err)
|
2026-03-17 19:27:44 +00:00
|
|
|
}
|
2026-03-16 11:10:33 +00:00
|
|
|
|
|
|
|
|
var result []PRInfo
|
|
|
|
|
for _, pr := range prs {
|
2026-03-29 20:15:58 +00:00
|
|
|
prState := pr.State
|
|
|
|
|
if prState == "" {
|
|
|
|
|
prState = "open"
|
|
|
|
|
}
|
|
|
|
|
if state != "" && state != "all" && prState != state {
|
2026-03-22 14:15:41 +00:00
|
|
|
continue
|
|
|
|
|
}
|
2026-03-16 11:10:33 +00:00
|
|
|
var labels []string
|
|
|
|
|
for _, l := range pr.Labels {
|
|
|
|
|
labels = append(labels, l.Name)
|
|
|
|
|
}
|
|
|
|
|
result = append(result, PRInfo{
|
|
|
|
|
Repo: repo,
|
2026-03-29 20:15:58 +00:00
|
|
|
Number: int(pullRequestNumber(pr)),
|
2026-03-16 11:10:33 +00:00
|
|
|
Title: pr.Title,
|
2026-03-29 20:15:58 +00:00
|
|
|
State: prState,
|
|
|
|
|
Author: pullRequestAuthor(pr),
|
2026-03-16 11:10:33 +00:00
|
|
|
Branch: pr.Head.Ref,
|
|
|
|
|
Base: pr.Base.Ref,
|
|
|
|
|
Labels: labels,
|
|
|
|
|
Mergeable: pr.Mergeable,
|
|
|
|
|
URL: pr.HTMLURL,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|