Skip to content

Commit ec9b491

Browse files
committed
fixed Go mod dependancy issues
1 parent 9b00baa commit ec9b491

3 files changed

Lines changed: 191 additions & 8 deletions

File tree

internal/agentic/autonomous.go

Lines changed: 133 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/user/terminal-intelligence/internal/ai"
1414
)
1515

16-
var projectNameRe = regexp.MustCompile(`(?im)^[#*\-\s]*project\s*name\s*[:\-]?\s*([a-z0-9\-]+)`)
16+
var projectNameRe = regexp.MustCompile(`(?im)project\s*name\s*[:\-]?\s*` + "`?" + `([a-z0-9\-_]+)` + "`?")
1717

1818
// CreatorState represents the current state of the Autonomous Creator state machine.
1919
type CreatorState int
@@ -119,13 +119,43 @@ Please provide an implementation plan. Include:
119119
}
120120

121121
func (c *AutonomousCreator) doSetup() (string, error) {
122+
// Check if project directory already exists and find an available name
123+
originalName := c.ProjectName
124+
counter := 1
125+
126+
for {
127+
if _, err := os.Stat(c.ProjectDir); os.IsNotExist(err) {
128+
// Directory doesn't exist, we can use it
129+
break
130+
}
131+
132+
// Directory exists, try with a number suffix
133+
c.ProjectName = fmt.Sprintf("%s-%d", originalName, counter)
134+
c.ProjectDir = filepath.Join(c.Workspace, c.ProjectName)
135+
counter++
136+
137+
// Safety check to avoid infinite loop
138+
if counter > 100 {
139+
return "", fmt.Errorf("too many existing project directories with name %s", originalName)
140+
}
141+
}
142+
122143
// Create project directory
123144
if err := os.MkdirAll(c.ProjectDir, 0755); err != nil {
124145
return "", fmt.Errorf("failed to create project directory: %v", err)
125146
}
126147

148+
var message string
149+
if c.ProjectName != originalName {
150+
message = fmt.Sprintf("ai-assist %s\nNote: Directory '%s' already exists.\nCreated project folder: %s\n\nMoving to install dependencies...",
151+
getCurrentTime(), originalName, c.ProjectName)
152+
} else {
153+
message = fmt.Sprintf("ai-assist %s\nCreated project folder: %s\n\nMoving to install dependencies...",
154+
getCurrentTime(), c.ProjectName)
155+
}
156+
127157
c.State = StateDependencies
128-
return fmt.Sprintf("ai-assist %s\nCreated project folder: %s\n\nMoving to install dependencies...", getCurrentTime(), c.ProjectName), nil
158+
return message, nil
129159
}
130160

131161
func (c *AutonomousCreator) doDependencies() (string, error) {
@@ -258,6 +288,14 @@ Only return the file paths and code blocks. No other text.`, c.Plan)
258288
createdFiles = append(createdFiles, relPath)
259289
}
260290

291+
// Post-creation dependency resolution for Go projects
292+
projectType := c.detectProjectType()
293+
if projectType == "Go" {
294+
if err := c.runGoModTidy(); err != nil {
295+
return "", fmt.Errorf("failed to run go mod tidy: %v", err)
296+
}
297+
}
298+
261299
c.State = StateTesting
262300
return fmt.Sprintf("ai-assist %s\nGenerated and saved %d files:\n- %s\n\nMoving to testing...", getCurrentTime(), len(createdFiles), strings.Join(createdFiles, "\n- ")), nil
263301
}
@@ -319,9 +357,8 @@ Return ONLY this single bash command, no formatting, no markdown.`,
319357
os.Remove(scriptPath)
320358

321359
if err != nil {
322-
// On error we let the AI try to fix it, or simply return the error and halt.
323-
// Implementing self-healing here goes beyond simple flow, but we can do a single pass:
324-
return "", fmt.Errorf("Automated test failed: %v\nOutput: %s\n\nAborting autonomous creation.", err, string(out))
360+
// Try to fix the error automatically
361+
return c.attemptTestFix(projectType, cmdStr, string(out), err)
325362
}
326363
}
327364

@@ -655,10 +692,36 @@ func aicall(client ai.AIClient, model, prompt string) (string, error) {
655692
}
656693

657694
func extractProjectName(plan string) string {
695+
// Try the standard "Project Name:" format first
658696
matches := projectNameRe.FindStringSubmatch(plan)
659697
if len(matches) >= 2 && matches[1] != "" {
660-
return strings.TrimSpace(matches[1])
698+
name := strings.TrimSpace(matches[1])
699+
// Remove backticks if present
700+
name = strings.Trim(name, "`")
701+
return name
702+
}
703+
704+
// Try to find project name in backticks on its own line
705+
// Pattern: `project-name` on a line by itself or after "Project Name"
706+
backticksRe := regexp.MustCompile("(?m)`([a-z0-9][a-z0-9\\-_]*)`")
707+
backticksMatches := backticksRe.FindAllStringSubmatch(plan, -1)
708+
709+
// Look for the first backtick-enclosed name that looks like a project name
710+
for _, match := range backticksMatches {
711+
if len(match) >= 2 {
712+
name := match[1]
713+
// Check if it looks like a project name (contains hyphens or underscores)
714+
if strings.Contains(name, "-") || strings.Contains(name, "_") {
715+
return name
716+
}
717+
}
661718
}
719+
720+
// If we found any backtick name, use the first one
721+
if len(backticksMatches) > 0 && len(backticksMatches[0]) >= 2 {
722+
return backticksMatches[0][1]
723+
}
724+
662725
return "autonomous-app"
663726
}
664727

@@ -822,3 +885,67 @@ func (c *AutonomousCreator) findMainPowerShellFile() string {
822885

823886
return ""
824887
}
888+
889+
// runGoModTidy runs go mod tidy to download dependencies
890+
func (c *AutonomousCreator) runGoModTidy() error {
891+
cmd := exec.Command("go", "mod", "tidy")
892+
cmd.Dir = c.ProjectDir
893+
output, err := cmd.CombinedOutput()
894+
if err != nil {
895+
return fmt.Errorf("go mod tidy failed: %v\nOutput: %s", err, string(output))
896+
}
897+
return nil
898+
}
899+
900+
// attemptTestFix tries to automatically fix test failures
901+
func (c *AutonomousCreator) attemptTestFix(projectType, testCmd, output string, testErr error) (string, error) {
902+
var result strings.Builder
903+
result.WriteString(fmt.Sprintf("ai-assist %s\nTest failed. Attempting automatic fix...\n\n", getCurrentTime()))
904+
result.WriteString(fmt.Sprintf("Error: %v\n", testErr))
905+
result.WriteString(fmt.Sprintf("Output:\n%s\n\n", output))
906+
907+
// Check for common Go dependency issues
908+
if projectType == "Go" && strings.Contains(output, "missing go.sum entry") {
909+
result.WriteString("Detected missing dependencies. Running 'go mod tidy'...\n")
910+
911+
if err := c.runGoModTidy(); err != nil {
912+
return "", fmt.Errorf("automatic fix failed: %v", err)
913+
}
914+
915+
result.WriteString("Dependencies resolved. Retrying test...\n\n")
916+
917+
// Retry the test
918+
scriptPath := filepath.Join(c.ProjectDir, "test.sh")
919+
if runtime.GOOS == "windows" {
920+
scriptPath = filepath.Join(c.ProjectDir, "test.bat")
921+
}
922+
923+
err := os.WriteFile(scriptPath, []byte(testCmd), 0755)
924+
if err != nil {
925+
return "", fmt.Errorf("failed to write retry test script: %v", err)
926+
}
927+
928+
var cmd *exec.Cmd
929+
if runtime.GOOS == "windows" {
930+
cmd = exec.Command("cmd", "/C", "test.bat")
931+
} else {
932+
cmd = exec.Command("sh", "test.sh")
933+
}
934+
cmd.Dir = c.ProjectDir
935+
retryOut, retryErr := cmd.CombinedOutput()
936+
os.Remove(scriptPath)
937+
938+
if retryErr != nil {
939+
result.WriteString(fmt.Sprintf("Retry failed: %v\n", retryErr))
940+
result.WriteString(fmt.Sprintf("Output:\n%s\n\n", string(retryOut)))
941+
return "", fmt.Errorf("automated test failed after fix attempt:\n%s", result.String())
942+
}
943+
944+
result.WriteString("✓ Test passed after automatic fix!\n\n")
945+
c.State = StateDocumentation
946+
return result.String() + fmt.Sprintf("ai-assist %s\nMoving to documentation...", getCurrentTime()), nil
947+
}
948+
949+
// For other errors, just report and abort
950+
return "", fmt.Errorf("automated test failed: %v\nOutput: %s\n\nAborting autonomous creation.", testErr, output)
951+
}

internal/agentic/autonomous_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,51 @@ func TestFindMainPowerShellFile(t *testing.T) {
318318
})
319319
}
320320
}
321+
322+
func TestExtractProjectName(t *testing.T) {
323+
tests := []struct {
324+
name string
325+
plan string
326+
expected string
327+
}{
328+
{
329+
name: "Standard format with colon",
330+
plan: `### 1. Project Name: my-app`,
331+
expected: "my-app",
332+
},
333+
{
334+
name: "Backticks format",
335+
plan: "### 1. Project Name\n`go-time-app`",
336+
expected: "go-time-app",
337+
},
338+
{
339+
name: "Backticks with underscores",
340+
plan: "Project Name: `sys_stats`",
341+
expected: "sys_stats",
342+
},
343+
{
344+
name: "Multiple backticks, use first with hyphens",
345+
plan: "`go-time-app`\nSome text\n`another`",
346+
expected: "go-time-app",
347+
},
348+
{
349+
name: "No project name found",
350+
plan: "This is a plan without a project name",
351+
expected: "autonomous-app",
352+
},
353+
{
354+
name: "Project name with numbers",
355+
plan: "Project Name: `app-v2`",
356+
expected: "app-v2",
357+
},
358+
}
359+
360+
for _, tt := range tests {
361+
t.Run(tt.name, func(t *testing.T) {
362+
result := extractProjectName(tt.plan)
363+
if result != tt.expected {
364+
t.Errorf("extractProjectName() = %v, want %v", result, tt.expected)
365+
}
366+
})
367+
}
368+
}

internal/ui/aichat.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -682,13 +682,18 @@ func (a *AIChatPane) ensureTiDirInGitignore() {
682682
// appendMessageToSessionLog appends a new message to the automated session log file.
683683
func (a *AIChatPane) appendMessageToSessionLog(msg types.ChatMessage) {
684684
if a.workspaceRoot == "" {
685+
// Log to stderr for debugging
686+
fmt.Fprintf(os.Stderr, "Warning: workspaceRoot is empty, cannot save session\n")
685687
return
686688
}
687689

688690
tiDir := filepath.Join(a.workspaceRoot, ".ti")
689691
tiDirCreated := false
690692
if _, err := os.Stat(tiDir); os.IsNotExist(err) {
691-
os.MkdirAll(tiDir, 0755)
693+
if err := os.MkdirAll(tiDir, 0755); err != nil {
694+
fmt.Fprintf(os.Stderr, "Error creating .ti directory: %v\n", err)
695+
return
696+
}
692697
tiDirCreated = true
693698
}
694699

@@ -703,6 +708,7 @@ func (a *AIChatPane) appendMessageToSessionLog(msg types.ChatMessage) {
703708

704709
f, err := os.OpenFile(a.sessionFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
705710
if err != nil {
711+
fmt.Fprintf(os.Stderr, "Error opening session file %s: %v\n", a.sessionFile, err)
706712
return
707713
}
708714
defer f.Close()
@@ -715,7 +721,9 @@ func (a *AIChatPane) appendMessageToSessionLog(msg types.ChatMessage) {
715721
content.WriteString(msg.Content)
716722
content.WriteString("\n\n")
717723

718-
f.WriteString(content.String())
724+
if _, err := f.WriteString(content.String()); err != nil {
725+
fmt.Fprintf(os.Stderr, "Error writing to session file: %v\n", err)
726+
}
719727
}
720728

721729
// GetHistory returns conversation history.

0 commit comments

Comments
 (0)