mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-08-08 00:21:55 +00:00
108 lines
2.6 KiB
Go
108 lines
2.6 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"embed"
|
|
"log/slog"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
)
|
|
|
|
//go:embed templates/*.md
|
|
var templateFS embed.FS
|
|
|
|
// templateFiles lists the templates to seed, in order.
|
|
// BOOTSTRAP.md is handled separately (only seeded for brand-new workspaces).
|
|
var templateFiles = []string{
|
|
AgentsFile,
|
|
SoulFile,
|
|
ToolsFile,
|
|
IdentityFile,
|
|
UserFile,
|
|
CapabilitiesFile,
|
|
AgentsCoreFile,
|
|
AgentsTaskFile,
|
|
}
|
|
|
|
// ReadTemplate returns the content of an embedded template file.
|
|
func ReadTemplate(name string) (string, error) {
|
|
content, err := templateFS.ReadFile(templatePath(name))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(content), nil
|
|
}
|
|
|
|
// EnsureWorkspaceFiles seeds template files into a workspace directory.
|
|
// Only writes files that don't already exist (will not overwrite).
|
|
// BOOTSTRAP.md is only seeded if the workspace is brand new (no AGENTS.md exists).
|
|
// Returns the list of files that were created.
|
|
func EnsureWorkspaceFiles(workspaceDir string) ([]string, error) {
|
|
if err := os.MkdirAll(workspaceDir, 0755); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var created []string
|
|
|
|
// Check if this is a brand-new workspace (no AGENTS.md yet)
|
|
_, agentsErr := os.Stat(filepath.Join(workspaceDir, AgentsFile))
|
|
isBrandNew := os.IsNotExist(agentsErr)
|
|
|
|
// Seed standard template files
|
|
for _, name := range templateFiles {
|
|
ok, err := seedTemplate(workspaceDir, name)
|
|
if err != nil {
|
|
slog.Warn("bootstrap: failed to seed template", "file", name, "error", err)
|
|
continue
|
|
}
|
|
if ok {
|
|
created = append(created, name)
|
|
}
|
|
}
|
|
|
|
// Seed BOOTSTRAP.md only for brand-new workspaces
|
|
if isBrandNew {
|
|
ok, err := seedTemplate(workspaceDir, BootstrapFile)
|
|
if err != nil {
|
|
slog.Warn("bootstrap: failed to seed BOOTSTRAP.md", "error", err)
|
|
} else if ok {
|
|
created = append(created, BootstrapFile)
|
|
}
|
|
}
|
|
|
|
return created, nil
|
|
}
|
|
|
|
// seedTemplate writes a template file to the workspace if it doesn't exist.
|
|
// Returns true if the file was created, false if it already exists.
|
|
func seedTemplate(workspaceDir, name string) (bool, error) {
|
|
dstPath := filepath.Join(workspaceDir, name)
|
|
|
|
// Only create if file doesn't exist (O_EXCL)
|
|
f, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
|
|
if err != nil {
|
|
if os.IsExist(err) {
|
|
return false, nil // already exists, skip
|
|
}
|
|
return false, err
|
|
}
|
|
defer f.Close()
|
|
|
|
// Read embedded template
|
|
content, err := templateFS.ReadFile(templatePath(name))
|
|
if err != nil {
|
|
os.Remove(dstPath) // clean up empty file
|
|
return false, err
|
|
}
|
|
|
|
if _, err := f.Write(content); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func templatePath(name string) string {
|
|
return path.Join("templates", name)
|
|
}
|