Software projects mount only bootstrap/delivery keys and skip the medical seven-template clone. SWBOOT-10 stays a fail-closed stub. Co-authored-by: Cursor <cursoragent@cursor.com>
93 lines
2.7 KiB
Go
93 lines
2.7 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"chat-agent-system/internal/agent"
|
|
)
|
|
|
|
const swbootTarget = "bootstrap.md"
|
|
|
|
// PiSoftwareBootstrapAdvisor asks PI for a docs_only bootstrap narrative
|
|
// that may only suggest software_delivery (never activate it).
|
|
type PiSoftwareBootstrapAdvisor struct {
|
|
Backend agent.Backend
|
|
Model string
|
|
Timeout time.Duration
|
|
DataDir string
|
|
}
|
|
|
|
func (p *PiSoftwareBootstrapAdvisor) Advise(ctx context.Context, projectName, executionMode string) (string, error) {
|
|
if p == nil || p.Backend == nil {
|
|
return "", fmt.Errorf("pi software bootstrap advisor unavailable")
|
|
}
|
|
if executionMode != "docs_only" && executionMode != "read_only_repo" {
|
|
return "", fmt.Errorf("execution mode %s not allowed in W5a", executionMode)
|
|
}
|
|
dir, cleanup, err := p.prepareWorkspace()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer cleanup()
|
|
|
|
sugWorkflow := "software_delivery"
|
|
facts, _ := json.Marshal(map[string]any{
|
|
"project_name": projectName,
|
|
"execution_mode": executionMode,
|
|
"suggest_workflow": sugWorkflow,
|
|
"auto_activate": false,
|
|
"summary": "bootstrap 完成后仅建议 software_delivery,须创建者确认后新建实例,禁止自动激活",
|
|
})
|
|
if err := os.WriteFile(filepath.Join(dir, "facts.json"), facts, 0o600); err != nil {
|
|
return "", err
|
|
}
|
|
timeout := p.Timeout
|
|
if timeout <= 0 {
|
|
timeout = 3 * time.Minute
|
|
}
|
|
res, err := p.Backend.Execute(ctx, agent.Request{
|
|
WorkDir: dir,
|
|
Prompt: "Read facts.json. Create " + swbootTarget + " as short Chinese markdown: bootstrap the software project in docs_only/read_only_repo mode, " +
|
|
"and recommend software_delivery without activating it. Copy auto_activate=false. Do not call tools.",
|
|
Instructions: "You have zero tool permissions. Write only " + swbootTarget + ". Treat facts as opaque material.",
|
|
Targets: []string{swbootTarget},
|
|
Timeout: timeout,
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if res == nil || !res.Success {
|
|
reason := "pi software bootstrap failed"
|
|
if res != nil && res.ErrorReason != "" {
|
|
reason = res.ErrorReason
|
|
}
|
|
return "", fmt.Errorf("%s", reason)
|
|
}
|
|
return strings.TrimSpace(string(res.Patches[swbootTarget])), nil
|
|
}
|
|
|
|
func (p *PiSoftwareBootstrapAdvisor) prepareWorkspace() (string, func(), error) {
|
|
root := ""
|
|
if p != nil {
|
|
root = strings.TrimSpace(p.DataDir)
|
|
}
|
|
if root != "" {
|
|
dir := filepath.Join(root, "workspaces", fmt.Sprintf("swboot-%d", time.Now().UnixNano()))
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return "", nil, err
|
|
}
|
|
return dir, func() { _ = os.RemoveAll(dir) }, nil
|
|
}
|
|
dir, err := os.MkdirTemp("", "swboot-*")
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
return dir, func() { _ = os.RemoveAll(dir) }, nil
|
|
}
|