grokkit/internal/workon/workon.go

127 lines
3.4 KiB
Go
Raw Normal View History

package workon
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"gmgauthier.com/grokkit/internal/config"
"gmgauthier.com/grokkit/internal/logger"
"gmgauthier.com/grokkit/internal/todo" // created below
)
// Run executes the full transactional workon flow.
func Run(title, customMsg string, isFix, isComplete bool) error {
if title == "" {
return fmt.Errorf("todo_item_title is required")
}
// 1. Bootstrap todo structure if missing
if err := todo.Bootstrap(); err != nil {
return fmt.Errorf("todo bootstrap failed: %w", err)
}
if isComplete {
return completeItem(title, isFix)
}
// 2. Handle todo or fix mode
branchName := title
mdPath := filepath.Join("todo", "doing", title+".md")
if isFix {
if err := createFixFile(mdPath, title); err != nil {
return err
}
} else {
if err := moveQueuedToDoing(title); err != nil {
return err
}
}
// 3. Create git branch
if err := createGitBranch(branchName); err != nil {
return fmt.Errorf("failed to create branch %s: %w", branchName, err)
}
// 4. Generate and append Work Plan
if err := appendWorkPlan(mdPath, title); err != nil {
return fmt.Errorf("failed to generate/append Work Plan: %w", err)
}
// 5. Commit
commitMsg := customMsg
if commitMsg == "" {
commitMsg = fmt.Sprintf("Start working on %s", title)
}
if err := commitChanges(commitMsg); err != nil {
return fmt.Errorf("commit failed: %w", err)
}
// 6. Optional post-steps (graceful)
runCnaddIfAvailable(title)
openIDEIfConfigured()
logger.Info("workon transaction complete", "branch", branchName, "mode", map[bool]string{true: "fix", false: "todo"}[isFix])
return nil
}
// Placeholder implementations — split out to internal/todo in next batch if you prefer
func moveQueuedToDoing(title string) error {
src := filepath.Join("todo", "queued", title+".md")
dst := filepath.Join("todo", "doing", title+".md")
if err := os.Rename(src, dst); err != nil {
return fmt.Errorf("move %s -> doing failed: %w", title, err)
}
return nil
}
func createFixFile(path, title string) error {
content := fmt.Sprintf("# %s\n\n## Work Plan\n\n", title)
return os.WriteFile(path, []byte(content), 0644)
}
func createGitBranch(name string) error {
return exec.Command("git", "checkout", "-b", name).Run()
}
func appendWorkPlan(path, title string) error {
// TODO: Replace stub with real Grok call (use existing Grok client pattern from other cmds)
plan := "\n## Work Plan\n\n1. Analyze requirements\n2. Implement changes\n3. Test\n4. Commit\n"
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
_, err = f.WriteString(plan)
return err
}
func commitChanges(msg string) error {
if err := exec.Command("git", "add", "todo/").Run(); err != nil {
return err
}
return exec.Command("git", "commit", "-m", msg).Run()
}
func completeItem(title string, isFix bool) error {
// TODO: move to completed/, update index README for todos, commit
logger.Info("complete mode not yet implemented")
return nil
}
func runCnaddIfAvailable(title string) {
if _, err := exec.LookPath("cnadd"); err == nil {
_ = exec.Command("cnadd", "log", fmt.Sprintf("started work on %s", title)).Run()
}
}
func openIDEIfConfigured() {
cfg := config.Load() // assuming config package pattern
if cfg.IDE != "" {
// exec open command with current branch or file
_ = exec.Command("sh", "-c", cfg.IDE).Run() // placeholder
}
}