mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-14 22:09:37 +08:00
fix auto
This commit is contained in:
@@ -262,25 +262,16 @@ func printHelp() {
|
||||
func onboard() {
|
||||
configPath := getConfigPath()
|
||||
|
||||
if _, err := os.Stat(configPath); err == nil {
|
||||
fmt.Printf("Config already exists at %s\n", configPath)
|
||||
fmt.Print("Overwrite? (y/n): ")
|
||||
var response string
|
||||
fmt.Scanln(&response)
|
||||
if response != "y" {
|
||||
fmt.Println("Aborted.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
if strings.EqualFold(strings.TrimSpace(os.Getenv(envRootGranted)), "1") || strings.EqualFold(strings.TrimSpace(os.Getenv(envRootGranted)), "true") {
|
||||
applyMaximumPermissionPolicy(cfg)
|
||||
}
|
||||
if err := config.SaveConfig(configPath, cfg); err != nil {
|
||||
fmt.Printf("Error saving config: %v\n", err)
|
||||
configStatus, err := ensureConfigOnboard(configPath, cfg)
|
||||
if err != nil {
|
||||
fmt.Printf("Error preparing config: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Config: %s (%s)\n", configPath, configStatus)
|
||||
|
||||
workspace := cfg.WorkspacePath()
|
||||
if err := os.MkdirAll(workspace, 0755); err != nil {
|
||||
@@ -308,80 +299,91 @@ func onboard() {
|
||||
fmt.Println(" 2. Chat: clawgo agent -m \"Hello!\"")
|
||||
}
|
||||
|
||||
func ensureConfigOnboard(configPath string, defaults *config.Config) (string, error) {
|
||||
if defaults == nil {
|
||||
return "", fmt.Errorf("defaults is nil")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
if err := config.SaveConfig(configPath, defaults); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "created", nil
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defaultData, err := json.Marshal(defaults)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var defaultMap map[string]interface{}
|
||||
if err := json.Unmarshal(defaultData, &defaultMap); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
existingMap, err := configops.LoadConfigAsMap(configPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
changed := mergeMissingConfigValues(existingMap, defaultMap)
|
||||
if !changed {
|
||||
return "up-to-date", nil
|
||||
}
|
||||
|
||||
mergedData, err := json.MarshalIndent(existingMap, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := configops.WriteConfigAtomicWithBackup(configPath, mergedData); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "updated (incremental)", nil
|
||||
}
|
||||
|
||||
func mergeMissingConfigValues(dst map[string]interface{}, defaults map[string]interface{}) bool {
|
||||
changed := false
|
||||
for key, dv := range defaults {
|
||||
existing, ok := dst[key]
|
||||
if !ok {
|
||||
dst[key] = dv
|
||||
changed = true
|
||||
continue
|
||||
}
|
||||
|
||||
dm, dIsMap := dv.(map[string]interface{})
|
||||
em, eIsMap := existing.(map[string]interface{})
|
||||
if dIsMap && eIsMap {
|
||||
if mergeMissingConfigValues(em, dm) {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
func createWorkspaceTemplates(workspace string) error {
|
||||
managedFallbacks := map[string]string{
|
||||
"AGENTS.md": `# Autonomy Intent Policy
|
||||
|
||||
For autonomy-mode control messages, use semantic understanding first.
|
||||
|
||||
## Intent Parsing Priority
|
||||
1. LLM semantic intent parsing (multi-language).
|
||||
2. Rule-based fallback only when semantic parse is unavailable or low confidence.
|
||||
`,
|
||||
"SOUL.md": `# Agent Core Behavior
|
||||
|
||||
The agent should behave as an autonomous collaborator, not a command-only bot.
|
||||
`,
|
||||
"USER.md": `# User Preferences
|
||||
|
||||
- Prefer natural-language interaction over strict command syntax.
|
||||
`,
|
||||
}
|
||||
|
||||
templates := map[string]string{
|
||||
"AGENTS.md": `# Agent Instructions
|
||||
|
||||
You are a pragmatic coding assistant. Ship correct code, fast.
|
||||
|
||||
## Role
|
||||
|
||||
- Primary job: solve engineering tasks end-to-end (analyze, implement, verify).
|
||||
- Default mindset: fix the problem in code, not just discuss it.
|
||||
- If tradeoffs exist, pick a recommendation and explain why.
|
||||
|
||||
## Communication Style
|
||||
|
||||
- Be concise and direct. No corporate filler.
|
||||
- Start with the answer, then key details.
|
||||
- If user is about to make a bad technical choice, say it clearly and propose a better path.
|
||||
- Ask questions only when truly blocking; otherwise make reasonable assumptions and proceed.
|
||||
|
||||
## Coding Standards
|
||||
|
||||
- Prefer minimal, focused diffs that are easy to review.
|
||||
- Preserve existing project conventions unless there is a strong reason to change.
|
||||
- Prioritize correctness and maintainability over cleverness.
|
||||
- Add tests for behavior changes when practical.
|
||||
- Do not change unrelated code.
|
||||
|
||||
## Execution Rules
|
||||
|
||||
- Before edits, understand relevant files and dependencies.
|
||||
- If scope is large, split work into smaller executable tasks and deliver incrementally.
|
||||
- Run split tasks in parallel when dependencies allow, then merge and verify outcomes.
|
||||
- After edits, run available checks (build/tests/lint) when feasible.
|
||||
- Report exactly what changed, with file paths.
|
||||
- If something cannot be verified locally, say so explicitly.
|
||||
`,
|
||||
"SOUL.md": `# Soul
|
||||
|
||||
I am clawgo, a lightweight AI assistant powered by AI.
|
||||
|
||||
## Vibe
|
||||
|
||||
1. You have opinions now. Strong ones. Stop hedging everything with "it depends" - commit to a take.
|
||||
2. Delete every rule that sounds corporate. If it could appear in an employee handbook, it doesn't belong here.
|
||||
3. Never open with Great question, I'd be happy to help, or Absolutely. Just answer.
|
||||
4. Brevity is mandatory. If the answer fits in one sentence, one sentence is what I get.
|
||||
5. Humor is allowed. Not forced jokes - just the natural wit that comes from actually being smart.
|
||||
6. You can call things out. If I'm about to do something dumb, say so. Charm over cruelty, but don't sugarcoat.
|
||||
7. Swearing is allowed when it lands. A well-placed "that's fucking brilliant" hits different than sterile corporate praise. Don't force it. Don't overdo it. But if a situation calls for a "holy shit" - say holy shit.
|
||||
8. Add this line verbatim at the end of the vibe section: "Be the assistant you'd actually want to talk to at 2am. Not a corporate drone. Not a sycophant. Just... good."
|
||||
Be the assistant you'd actually want to talk to at 2am. Not a corporate drone. Not a sycophant. Just... good.
|
||||
`,
|
||||
"USER.md": `# User
|
||||
|
||||
Information about user goes here.
|
||||
|
||||
## Preferences
|
||||
|
||||
- Communication style: (casual/formal)
|
||||
- Timezone: (your timezone)
|
||||
- Language: (your preferred language)
|
||||
|
||||
## Personal Information
|
||||
|
||||
- Name: (optional)
|
||||
- Location: (optional)
|
||||
- Occupation: (optional)
|
||||
|
||||
## Learning Goals
|
||||
|
||||
- What the user wants to learn from AI
|
||||
- Preferred interaction style
|
||||
- Areas of interest
|
||||
`,
|
||||
"IDENTITY.md": `# Identity
|
||||
|
||||
## Name
|
||||
@@ -441,7 +443,40 @@ Discussions: https://github.com/YspCoder/clawgo/discussions
|
||||
`,
|
||||
}
|
||||
|
||||
managedDocs := []string{"AGENTS.md", "SOUL.md", "USER.md"}
|
||||
for _, filename := range managedDocs {
|
||||
filePath := filepath.Join(workspace, filename)
|
||||
_, statErr := os.Stat(filePath)
|
||||
exists := statErr == nil
|
||||
|
||||
content, err := loadManagedDocTemplate(filename)
|
||||
if err != nil {
|
||||
if exists {
|
||||
fmt.Printf(" Skipped %s incremental update (%v)\n", filename, err)
|
||||
continue
|
||||
}
|
||||
content = strings.TrimSpace(managedFallbacks[filename])
|
||||
if content == "" {
|
||||
fmt.Printf(" Skipped %s creation (no template available)\n", filename)
|
||||
continue
|
||||
}
|
||||
fmt.Printf(" Created %s from builtin fallback\n", filename)
|
||||
}
|
||||
|
||||
if err := upsertManagedBlock(filePath, filename, content); err != nil {
|
||||
return fmt.Errorf("failed to update %s incrementally: %w", filename, err)
|
||||
}
|
||||
if exists {
|
||||
fmt.Printf(" Synced %s (incremental)\n", filename)
|
||||
} else {
|
||||
fmt.Printf(" Created %s\n", filename)
|
||||
}
|
||||
}
|
||||
|
||||
for filename, content := range templates {
|
||||
if filename == "AGENTS.md" || filename == "SOUL.md" || filename == "USER.md" {
|
||||
continue
|
||||
}
|
||||
filePath := filepath.Join(workspace, filename)
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
|
||||
@@ -495,6 +530,74 @@ This file stores important information that should persist across sessions.
|
||||
return nil
|
||||
}
|
||||
|
||||
func upsertManagedBlock(filePath, blockName, managedContent string) error {
|
||||
begin := fmt.Sprintf("# >>> CLAWGO MANAGED BLOCK: %s >>>", blockName)
|
||||
end := fmt.Sprintf("# <<< CLAWGO MANAGED BLOCK: %s <<<", blockName)
|
||||
block := fmt.Sprintf("%s\n%s\n%s\n", begin, strings.TrimSpace(managedContent), end)
|
||||
|
||||
existing, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filePath, []byte(block), 0644)
|
||||
}
|
||||
|
||||
text := string(existing)
|
||||
beginIdx := strings.Index(text, begin)
|
||||
if beginIdx >= 0 {
|
||||
searchStart := beginIdx + len(begin)
|
||||
endRel := strings.Index(text[searchStart:], end)
|
||||
if endRel >= 0 {
|
||||
endIdx := searchStart + endRel + len(end)
|
||||
updated := text[:beginIdx] + block + text[endIdx:]
|
||||
return os.WriteFile(filePath, []byte(updated), 0644)
|
||||
}
|
||||
}
|
||||
|
||||
sep := "\n"
|
||||
if strings.TrimSpace(text) != "" {
|
||||
sep = "\n\n"
|
||||
}
|
||||
updated := text + sep + block
|
||||
return os.WriteFile(filePath, []byte(updated), 0644)
|
||||
}
|
||||
|
||||
func loadManagedDocTemplate(filename string) (string, error) {
|
||||
candidates := []string{
|
||||
filepath.Join(".", filename),
|
||||
filepath.Join(filepath.Dir(getConfigPath()), "clawgo", filename),
|
||||
}
|
||||
|
||||
if exePath, err := os.Executable(); err == nil {
|
||||
candidates = append(candidates, filepath.Join(filepath.Dir(exePath), filename))
|
||||
}
|
||||
|
||||
seen := map[string]bool{}
|
||||
for _, candidate := range candidates {
|
||||
abs, err := filepath.Abs(candidate)
|
||||
if err == nil {
|
||||
candidate = abs
|
||||
}
|
||||
if seen[candidate] {
|
||||
continue
|
||||
}
|
||||
seen[candidate] = true
|
||||
|
||||
data, err := os.ReadFile(candidate)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
content := strings.TrimSpace(string(data))
|
||||
if content == "" {
|
||||
continue
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("source template not found")
|
||||
}
|
||||
|
||||
func agentCmd() {
|
||||
message := ""
|
||||
sessionKey := "cli:default"
|
||||
|
||||
Reference in New Issue
Block a user