fix(agentic): run language-specific workspace prep

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 09:45:11 +00:00
parent 24bb3b26c6
commit c75911be67
2 changed files with 154 additions and 0 deletions

View file

@ -544,6 +544,9 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques
}
s.cloneWorkspaceDeps(ctx, workspaceDir, repoDir, input.Org)
if err := s.runWorkspaceLanguagePrep(ctx, workspaceDir, repoDir); err != nil {
return nil, PrepOutput{}, err
}
docsDir := core.JoinPath(workspaceDir, ".core", "reference", "docs")
if !fs.IsDir(docsDir) {
@ -696,6 +699,39 @@ func (s *PrepSubsystem) buildPrompt(ctx context.Context, input PrepInput, branch
return b.String(), memories, consumers
}
// runWorkspaceLanguagePrep installs repository dependencies before the agent starts.
//
// _ = s.runWorkspaceLanguagePrep(ctx, "/srv/.core/workspace/core/go-io/task-42", "/srv/Code/core/go-io")
func (s *PrepSubsystem) runWorkspaceLanguagePrep(ctx context.Context, workspaceDir, repoDir string) error {
process := s.Core().Process()
if fs.IsFile(core.JoinPath(repoDir, "go.mod")) {
if result := process.RunIn(ctx, repoDir, "go", "mod", "download"); !result.OK {
return core.E("prepWorkspace", "go mod download failed", nil)
}
}
if fs.IsFile(core.JoinPath(repoDir, "go.mod")) && (fs.IsFile(core.JoinPath(workspaceDir, "go.work")) || fs.IsFile(core.JoinPath(repoDir, "go.work"))) {
if result := process.RunIn(ctx, repoDir, "go", "work", "sync"); !result.OK {
return core.E("prepWorkspace", "go work sync failed", nil)
}
}
if fs.IsFile(core.JoinPath(repoDir, "composer.json")) {
if result := process.RunIn(ctx, repoDir, "composer", "install"); !result.OK {
return core.E("prepWorkspace", "composer install failed", nil)
}
}
if fs.IsFile(core.JoinPath(repoDir, "package.json")) {
if result := process.RunIn(ctx, repoDir, "npm", "install"); !result.OK {
return core.E("prepWorkspace", "npm install failed", nil)
}
}
return nil
}
func (s *PrepSubsystem) getIssueBody(ctx context.Context, org, repo string, issue int) string {
idx := core.Sprintf("%d", issue)
iss, err := s.forge.Issues.Get(ctx, forge.Params{"owner": org, "repo": repo, "index": idx})

View file

@ -6,12 +6,14 @@ import (
"context"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
core "dappco.re/go/core"
"dappco.re/go/core/forge"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// --- Shutdown ---
@ -115,6 +117,122 @@ func TestPrep_FindConsumersList_Bad_NoGoWork(t *testing.T) {
assert.Empty(t, list)
}
func writeFakeLanguageCommand(t *testing.T, dir, name, logPath string, exitCode int) {
t.Helper()
script := core.Concat(
"#!/bin/sh\n",
"printf '%s %s\\n' '",
name,
"' \"$*\" >> '",
logPath,
"'\n",
"exit ",
core.Sprint(exitCode),
"\n",
)
require.True(t, fs.WriteMode(core.JoinPath(dir, name), script, 0755).OK)
}
// --- runWorkspaceLanguagePrep ---
func TestPrep_RunWorkspaceLanguagePrep_Good_Polyglot(t *testing.T) {
root := t.TempDir()
binDir := core.JoinPath(root, "bin")
require.True(t, fs.EnsureDir(binDir).OK)
logPath := core.JoinPath(root, "commands.log")
writeFakeLanguageCommand(t, binDir, "go", logPath, 0)
writeFakeLanguageCommand(t, binDir, "composer", logPath, 0)
writeFakeLanguageCommand(t, binDir, "npm", logPath, 0)
oldPath := os.Getenv("PATH")
t.Setenv("PATH", core.Concat(binDir, ":", oldPath))
workspaceDir := core.JoinPath(root, "workspace")
repoDir := core.JoinPath(root, "repo")
require.True(t, fs.EnsureDir(workspaceDir).OK)
require.True(t, fs.EnsureDir(repoDir).OK)
require.True(t, fs.Write(core.JoinPath(repoDir, "go.mod"), "module example.com/test\n").OK)
require.True(t, fs.Write(core.JoinPath(repoDir, "composer.json"), "{}").OK)
require.True(t, fs.Write(core.JoinPath(repoDir, "package.json"), "{}").OK)
require.True(t, fs.Write(core.JoinPath(workspaceDir, "go.work"), "go 1.22\n").OK)
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
err := s.runWorkspaceLanguagePrep(context.Background(), workspaceDir, repoDir)
require.NoError(t, err)
logResult := fs.Read(logPath)
require.True(t, logResult.OK)
assert.Equal(t, "go mod download\ngo work sync\ncomposer install\nnpm install\n", logResult.Value.(string))
}
func TestPrep_RunWorkspaceLanguagePrep_Bad_NoLanguageManifests(t *testing.T) {
root := t.TempDir()
binDir := core.JoinPath(root, "bin")
require.True(t, fs.EnsureDir(binDir).OK)
logPath := core.JoinPath(root, "commands.log")
writeFakeLanguageCommand(t, binDir, "go", logPath, 0)
writeFakeLanguageCommand(t, binDir, "composer", logPath, 0)
writeFakeLanguageCommand(t, binDir, "npm", logPath, 0)
oldPath := os.Getenv("PATH")
t.Setenv("PATH", core.Concat(binDir, ":", oldPath))
workspaceDir := core.JoinPath(root, "workspace")
repoDir := core.JoinPath(root, "repo")
require.True(t, fs.EnsureDir(workspaceDir).OK)
require.True(t, fs.EnsureDir(repoDir).OK)
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
err := s.runWorkspaceLanguagePrep(context.Background(), workspaceDir, repoDir)
require.NoError(t, err)
logResult := fs.Read(logPath)
assert.False(t, logResult.OK)
}
func TestPrep_RunWorkspaceLanguagePrep_Ugly_CommandFailure(t *testing.T) {
root := t.TempDir()
binDir := core.JoinPath(root, "bin")
require.True(t, fs.EnsureDir(binDir).OK)
logPath := core.JoinPath(root, "commands.log")
writeFakeLanguageCommand(t, binDir, "go", logPath, 0)
writeFakeLanguageCommand(t, binDir, "composer", logPath, 1)
writeFakeLanguageCommand(t, binDir, "npm", logPath, 0)
oldPath := os.Getenv("PATH")
t.Setenv("PATH", core.Concat(binDir, ":", oldPath))
workspaceDir := core.JoinPath(root, "workspace")
repoDir := core.JoinPath(root, "repo")
require.True(t, fs.EnsureDir(workspaceDir).OK)
require.True(t, fs.EnsureDir(repoDir).OK)
require.True(t, fs.Write(core.JoinPath(repoDir, "composer.json"), "{}").OK)
s := &PrepSubsystem{
ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}),
backoff: make(map[string]time.Time),
failCount: make(map[string]int),
}
err := s.runWorkspaceLanguagePrep(context.Background(), workspaceDir, repoDir)
require.Error(t, err)
assert.Contains(t, err.Error(), "composer install failed")
}
// --- pullWikiContent ---
func TestPrep_PullWikiContent_Good_WithPages(t *testing.T) {