@@ -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.
1919type CreatorState int
@@ -119,13 +119,43 @@ Please provide an implementation plan. Include:
119119}
120120
121121func (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\n Note: Directory '%s' already exists.\n Created project folder: %s\n \n Moving to install dependencies..." ,
151+ getCurrentTime (), originalName , c .ProjectName )
152+ } else {
153+ message = fmt .Sprintf ("ai-assist %s\n Created project folder: %s\n \n Moving to install dependencies..." ,
154+ getCurrentTime (), c .ProjectName )
155+ }
156+
127157 c .State = StateDependencies
128- return fmt . Sprintf ( "ai-assist %s \n Created project folder: %s \n \n Moving to install dependencies..." , getCurrentTime (), c . ProjectName ) , nil
158+ return message , nil
129159}
130160
131161func (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\n Generated and saved %d files:\n - %s\n \n Moving 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\n Output: %s\n \n Aborting 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
657694func 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\n Output: %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\n Test 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\n Moving to documentation..." , getCurrentTime ()), nil
947+ }
948+
949+ // For other errors, just report and abort
950+ return "" , fmt .Errorf ("automated test failed: %v\n Output: %s\n \n Aborting autonomous creation." , testErr , output )
951+ }
0 commit comments