From 781dd22144f41d2ea5528ff28387242e05eff9aa Mon Sep 17 00:00:00 2001 From: Steve Traugott Date: Sat, 9 Nov 2024 13:34:59 -0800 Subject: [PATCH] aidda: skip timestamp check for new output files --- .aidda/prompt | 2 +- .aidda/response | 139 +++++++++++++++++++++++++--------------------- v3/aidda/aidda.go | 17 +++--- 3 files changed, 86 insertions(+), 72 deletions(-) diff --git a/.aidda/prompt b/.aidda/prompt index 5f9a056..28aa97f 100644 --- a/.aidda/prompt +++ b/.aidda/prompt @@ -1,4 +1,4 @@ -aidda: comment out everything related to the test subcommand +aidda: skip timestamp check for new output files In: v3/aidda/aidda.go diff --git a/.aidda/response b/.aidda/response index bb99aa6..5d4ecce 100644 --- a/.aidda/response +++ b/.aidda/response @@ -34,7 +34,9 @@ XXX update this var ( baseDir string + promptFn string ignoreFn string + testFn string generateStampFn string commitStampFn string DefaultSysmsg = "You are an expert Go programmer. Please make the requested changes to the given code or documentation." @@ -114,67 +116,21 @@ func Do(g *core.Grokker, args ...string) (err error) { _, err = os.Stat(Spf("%s/.git", baseDir)) Ck(err) - // create a directory for aidda files // XXX location might want to be more flexible dir := Spf("%s/.aidda", baseDir) - err = os.MkdirAll(dir, 0755) - Ck(err) // generate filenames // XXX these should all be in a struct - promptFn := Spf("%s/prompt", dir) + promptFn = Spf("%s/prompt", dir) ignoreFn = Spf("%s/ignore", dir) - testFn := Spf("%s/test", dir) + testFn = Spf("%s/test", dir) generateStampFn = Spf("%s/generate.stamp", dir) commitStampFn = Spf("%s/commit.stamp", dir) - // Ensure there is an ignore file - err = ensureIgnoreFile(ignoreFn) - Ck(err) - - // Initialize Stamp instances for generate and commit + // Initialize Stamp objects for generate and commit generateStamp = NewStamp(generateStampFn) commitStamp = NewStamp(commitStampFn) - // Ensure timestamp files exist - now := time.Now() - err = generateStamp.Ensure(now) - Ck(err) - err = commitStamp.Ensure(now) - Ck(err) - - // If the test file is newer than any input files, then include - // the test results in the prompt; otherwise, clear the test file - testResults := "" - testStat, err := os.Stat(testFn) - if os.IsNotExist(err) { - err = nil - } else { - Ck(err) - // Get the list of input files - p, err := getPrompt(promptFn) - Ck(err) - inFns := p.In - // Check if the test file is newer than any input files - for _, fn := range inFns { - inStat, err := os.Stat(fn) - Ck(err) - if testStat.ModTime().After(inStat.ModTime()) { - // Include the test results in the prompt - buf, err := os.ReadFile(testFn) - Ck(err) - testResults = string(buf) - break - } - } - } - if len(testResults) == 0 { - // Clear the test file - Pl("Clearing test file") - err = os.WriteFile(testFn, []byte{}, 0644) - Ck(err) - } - // Determine if interactive mode is active isInteractive := false for _, cmd := range args { @@ -184,20 +140,20 @@ func Do(g *core.Grokker, args ...string) (err error) { } } - // Refactored loop: consume (shift) subcommands from args + // consume subcommands from args for len(args) > 0 { cmd := args[0] args = args[1:] Pl("aidda: running subcommand", cmd) switch cmd { case "init": - err = mkPrompt(promptFn) + err = initAidda(dir) Ck(err) case "menu": action, err := menu(g) Ck(err) - // Push the selected action to the args slice - args = append(args, action) + // Push the selected action to the front of args + args = append([]string{action}, args...) case "commit": // Check if prompt is newer than generate.stamp promptIsNewer, err := generateStamp.OlderThan(promptFn) @@ -238,14 +194,14 @@ func Do(g *core.Grokker, args ...string) (err error) { var p *Prompt p, err = getPrompt(promptFn) Ck(err) - err = generate(g, p, testResults) + err = generate(g, p) Ck(err) case "regenerate": // Regenerate code from the prompt without committing var p *Prompt p, err = getPrompt(promptFn) Ck(err) - err = generate(g, p, testResults) + err = generate(g, p) Ck(err) case "force-commit": // Commit using the current promptFn without checking @@ -292,7 +248,7 @@ func Do(g *core.Grokker, args ...string) (err error) { case "abort": // Abort the current operation Pl("Operation aborted by user.") - return fmt.Errorf("operation aborted") + return nil default: PrintUsageAndExit() } @@ -305,7 +261,7 @@ func PrintUsageAndExit() { fmt.Println("Usage: go run main.go {subcommand ...}") fmt.Println("Subcommands:") fmt.Println(" menu - Display the action menu") - fmt.Println(" init - Initialize the prompt file") + fmt.Println(" init - Initialize the .aidda directory") fmt.Println(" commit - Commit using the current prompt file contents as the commit message") fmt.Println(" generate - Generate changes from GPT based on the prompt") fmt.Println(" regenerate - Regenerate code from the prompt without committing") @@ -324,13 +280,29 @@ type Prompt struct { Txt string } -// mkPrompt function is responsible for creating a prompt file. -func mkPrompt(path string) (err error) { +// initAidda function is responsible for creating the .aidda directory and its contents +func initAidda(dir string) (err error) { defer Return(&err) + + // create a directory for aidda files + err = os.MkdirAll(dir, 0755) + Ck(err) + + // Ensure there is an ignore file + err = ensureIgnoreFile(ignoreFn) + Ck(err) + + // Ensure timestamp files exist + now := time.Now() + err = generateStamp.Ensure(now) + Ck(err) + err = commitStamp.Ensure(now) + Ck(err) + // Check if the file exists - _, err = os.Stat(path) + _, err = os.Stat(promptFn) if os.IsNotExist(err) { - err = createPromptFile(path) + err = createPromptFile(promptFn) Ck(err) } else { Ck(err) @@ -648,15 +620,19 @@ func runTest(fn string) (err error) { return err } -func generate(g *core.Grokker, p *Prompt, testResults string) (err error) { +func generate(g *core.Grokker, p *Prompt) (err error) { defer Return(&err) prompt := p.Txt Pl(prompt) + + testResults, err := getTestResults(testFn, p.In, p.Out) + Ck(err) if len(testResults) > 0 { Pl("Including test results in prompt") prompt = Spf("%s\n\n%s", p.Txt, testResults) } + inFns := p.In outFns := p.Out var outFls []core.FileLang @@ -671,7 +647,7 @@ func generate(g *core.Grokker, p *Prompt, testResults string) (err error) { sysmsg := p.Sysmsg if sysmsg == "" { - Pf("Sysmsg header missing, using default.") + Pl("Sysmsg header missing, using default.") sysmsg = DefaultSysmsg } Pf("Sysmsg: %s\n", sysmsg) @@ -915,12 +891,47 @@ func menu(g *core.Grokker) (action string, err error) { return "auto", nil case "x": Pl("Operation aborted by user.") - return "", fmt.Errorf("operation aborted") + return "", nil default: fmt.Printf("\nUnknown option: %s\n\n", string(char)) // Continue the loop to re-display the menu } } } + +// getTestResults reads the test file and returns its contents if it +// exists and is newer than all input and output files. If there are +// no new test results, then the test file is cleared and an empty +// string is returned. +func getTestResults(testFn string, inFns []string, outFns []string) (testResults string, err error) { + // get the test file's modification time + testStat, err := os.Stat(testFn) + if os.IsNotExist(err) { + err = nil + } else { + Ck(err) + // Check if the test file is newer than any input or output files + fns := append(inFns, outFns...) + for _, fn := range fns { + inStat, err := os.Stat(fn) + Ck(err) + if testStat.ModTime().After(inStat.ModTime()) { + // return the test file contents + buf, err := os.ReadFile(testFn) + Ck(err) + testResults = string(buf) + break + } + } + } + if len(testResults) == 0 { + // Clear the test file + Pl("Clearing test file") + err = os.WriteFile(testFn, []byte{}, 0644) + Ck(err) + } + + return testResults, err +} ``` EOF_/home/stevegt/lab/grokker/v3/aidda/aidda.go \ No newline at end of file diff --git a/v3/aidda/aidda.go b/v3/aidda/aidda.go index 759ca55..8b8028c 100644 --- a/v3/aidda/aidda.go +++ b/v3/aidda/aidda.go @@ -246,7 +246,7 @@ func Do(g *core.Grokker, args ...string) (err error) { case "abort": // Abort the current operation Pl("Operation aborted by user.") - return fmt.Errorf("operation aborted") + return nil default: PrintUsageAndExit() } @@ -264,9 +264,9 @@ func PrintUsageAndExit() { fmt.Println(" generate - Generate changes from GPT based on the prompt") fmt.Println(" regenerate - Regenerate code from the prompt without committing") fmt.Println(" force-commit - Commit changes without checking if the prompt has been updated") - fmt.Println(" test - Run tests and include the results in the prompt file") + fmt.Println(" test - Run tests and include the results in the next LLM prompt") fmt.Println(" auto - Automatically run generate or commit based on file timestamps") - fmt.Println(" abort - Abort the current operation") + fmt.Println(" abort - Abort subcommand processing") os.Exit(1) } @@ -856,9 +856,9 @@ func menu(g *core.Grokker) (action string, err error) { fmt.Println(" [r]egenerate - Regenerate code from the prompt without committing") fmt.Println(" [c]ommit - Commit using the current prompt file contents as the commit message") fmt.Println(" [f]orce-commit - Commit changes without checking if the prompt has been updated") - fmt.Println(" [t]est - Run tests and include the results in the prompt file") + fmt.Println(" [t]est - Run tests and include the results in the next LLM prompt") fmt.Println(" [a]uto - Automatically run generate or commit based on file timestamps") - fmt.Println(" e[x]it - Abort and exit the menu") + fmt.Println(" e[x]it - Abort and exit the menu") fmt.Println("Press the corresponding key to select an action...") // Initialize keyboard @@ -888,8 +888,7 @@ func menu(g *core.Grokker) (action string, err error) { case "a": return "auto", nil case "x": - Pl("Operation aborted by user.") - return "", fmt.Errorf("operation aborted") + return "abort", nil default: fmt.Printf("\nUnknown option: %s\n\n", string(char)) // Continue the loop to re-display the menu @@ -912,6 +911,10 @@ func getTestResults(testFn string, inFns []string, outFns []string) (testResults fns := append(inFns, outFns...) for _, fn := range fns { inStat, err := os.Stat(fn) + // skip if the file does not exist + if os.IsNotExist(err) { + continue + } Ck(err) if testStat.ModTime().After(inStat.ModTime()) { // return the test file contents