Replace placeholder with actual Grok client call to generate and append a tailored work plan section to todo/fix files. Add prompt engineering for concise, actionable plans. Include file content reading and model config integration. Update comments and error handling for clarity.
172 lines
4.9 KiB
Go
172 lines
4.9 KiB
Go
package workon
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"gmgauthier.com/grokkit/config"
|
|
"gmgauthier.com/grokkit/internal/grok"
|
|
"gmgauthier.com/grokkit/internal/logger"
|
|
"gmgauthier.com/grokkit/internal/todo"
|
|
)
|
|
|
|
// Run executes the full transactional workon flow per todo/doing/workon.md spec.
|
|
func Run(title, customMsg string, isFix, isComplete bool) error {
|
|
if title == "" {
|
|
return fmt.Errorf("todo_item_title is required")
|
|
}
|
|
|
|
logger.Info("workon starting", "title", title, "fix", isFix, "complete", isComplete)
|
|
|
|
// 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 fmt.Errorf("create fix file failed: %w", err)
|
|
}
|
|
} else {
|
|
if err := moveQueuedToDoing(title); err != nil {
|
|
return fmt.Errorf("move to doing failed: %w", err)
|
|
}
|
|
}
|
|
|
|
// 3. Create git branch (safe)
|
|
if err := createGitBranch(branchName); err != nil {
|
|
return fmt.Errorf("failed to create branch %s: %w", branchName, err)
|
|
}
|
|
|
|
// 4. Generate + append Work Plan via Grok
|
|
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
|
|
}
|
|
|
|
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 {
|
|
// Safe: if branch already exists, just checkout it; else create new
|
|
if err := exec.Command("git", "checkout", name).Run(); err == nil {
|
|
return nil
|
|
}
|
|
return exec.Command("git", "checkout", "-b", name).Run()
|
|
}
|
|
|
|
func appendWorkPlan(path, title string) error {
|
|
content := readFileContent(path)
|
|
|
|
prompt := fmt.Sprintf(`You are helping implement a todo item titled "%s".
|
|
|
|
Here is the current markdown content of the todo/fix file:
|
|
|
|
%s
|
|
|
|
Generate a concise, actionable **Work Plan** section.
|
|
Use numbered steps. Be specific to this item. Include testing and commit notes where relevant.
|
|
Output ONLY the markdown starting with "## Work Plan" — no extra text, no introduction.`, title, content)
|
|
|
|
// Real Grok call using the project's standard client (StreamSilent for clean output)
|
|
client := grok.NewClient()
|
|
model := config.GetModel("workon", "grok-4-fast-non-reasoning")
|
|
plan := client.StreamSilent([]map[string]string{
|
|
{"role": "system", "content": "You are a precise software engineering assistant."},
|
|
{"role": "user", "content": prompt},
|
|
}, model) // or pull model from config/env if available
|
|
|
|
// Append the plan
|
|
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if cerr := f.Close(); cerr != nil {
|
|
logger.Error("failed to close todo file", "err", cerr)
|
|
}
|
|
}()
|
|
_, err = f.WriteString(plan)
|
|
return err
|
|
}
|
|
|
|
func readFileContent(path string) string {
|
|
b, _ := os.ReadFile(path)
|
|
return string(b)
|
|
}
|
|
|
|
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 {
|
|
src := filepath.Join("todo", "doing", title+".md")
|
|
dst := filepath.Join("todo", "completed", title+".md")
|
|
if err := os.Rename(src, dst); err != nil {
|
|
return fmt.Errorf("move to completed failed: %w", err)
|
|
}
|
|
|
|
// TODO: update todo/README.md index under ## Completed (for non-fixes)
|
|
if !isFix {
|
|
logger.Info("TODO: update index README for completed todo")
|
|
}
|
|
|
|
commitMsg := fmt.Sprintf("Complete work on %s", title)
|
|
if err := commitChanges(commitMsg); err != nil {
|
|
return fmt.Errorf("complete commit failed: %w", err)
|
|
}
|
|
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() {
|
|
// TODO: implement via config once internal/config or root config supports IDE command
|
|
// For now, silent graceful fallback per spec
|
|
logger.Debug("IDE open skipped (config support pending)")
|
|
}
|