2026-03-31 19:30:05 +00:00
package workon
import (
2026-03-31 19:42:32 +00:00
"fmt"
"os"
"os/exec"
"path/filepath"
2026-03-31 19:30:05 +00:00
"gmgauthier.com/grokkit/internal/logger"
2026-03-31 19:56:45 +00:00
"gmgauthier.com/grokkit/internal/todo"
2026-03-31 20:04:40 +00:00
// TODO: Import the real Grok client once we wire it (standard pattern is internal/grok)
// e.g. "gmgauthier.com/grokkit/internal/grok"
2026-03-31 19:30:05 +00:00
)
2026-03-31 20:04:40 +00:00
// Run executes the full transactional workon flow per todo/doing/workon.md.
2026-03-31 19:42:32 +00:00
func Run ( title , customMsg string , isFix , isComplete bool ) error {
if title == "" {
return fmt . Errorf ( "todo_item_title is required" )
}
2026-03-31 19:56:45 +00:00
logger . Info ( "workon starting" , "title" , title , "fix" , isFix , "complete" , isComplete )
2026-03-31 20:04:40 +00:00
// 1. Bootstrap todo structure if missing
2026-03-31 19:42:32 +00:00
if err := todo . Bootstrap ( ) ; err != nil {
return fmt . Errorf ( "todo bootstrap failed: %w" , err )
}
if isComplete {
return completeItem ( title , isFix )
}
2026-03-31 20:04:40 +00:00
// 2. Handle todo or fix mode
2026-03-31 19:42:32 +00:00
branchName := title
mdPath := filepath . Join ( "todo" , "doing" , title + ".md" )
if isFix {
if err := createFixFile ( mdPath , title ) ; err != nil {
2026-03-31 19:56:45 +00:00
return fmt . Errorf ( "create fix file failed: %w" , err )
2026-03-31 19:42:32 +00:00
}
} else {
if err := moveQueuedToDoing ( title ) ; err != nil {
2026-03-31 19:56:45 +00:00
return fmt . Errorf ( "move to doing failed: %w" , err )
2026-03-31 19:42:32 +00:00
}
}
2026-03-31 19:30:05 +00:00
2026-03-31 19:56:45 +00:00
// 3. Create git branch (safe)
2026-03-31 19:42:32 +00:00
if err := createGitBranch ( branchName ) ; err != nil {
return fmt . Errorf ( "failed to create branch %s: %w" , branchName , err )
}
2026-03-31 19:30:05 +00:00
2026-03-31 20:04:40 +00:00
// 4. Generate and append Work Plan via Grok
2026-03-31 19:42:32 +00:00
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
}
2026-03-31 19:30:05 +00:00
2026-03-31 19:42:32 +00:00
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
}
2026-03-31 19:30:05 +00:00
2026-03-31 19:42:32 +00:00
func createFixFile ( path , title string ) error {
content := fmt . Sprintf ( "# %s\n\n## Work Plan\n\n" , title )
return os . WriteFile ( path , [ ] byte ( content ) , 0644 )
}
2026-03-31 19:30:05 +00:00
2026-03-31 19:42:32 +00:00
func createGitBranch ( name string ) error {
2026-03-31 20:04:40 +00:00
// Safe: if branch already exists, just checkout it
2026-03-31 19:56:45 +00:00
if err := exec . Command ( "git" , "checkout" , name ) . Run ( ) ; err == nil {
2026-03-31 20:04:40 +00:00
return nil
2026-03-31 19:56:45 +00:00
}
2026-03-31 19:42:32 +00:00
return exec . Command ( "git" , "checkout" , "-b" , name ) . Run ( )
}
2026-03-31 19:30:05 +00:00
2026-03-31 19:42:32 +00:00
func appendWorkPlan ( path , title string ) error {
2026-03-31 20:04:40 +00:00
// TODO: Replace with real Grok client call (internal/grok pattern from analyze/commit/etc.)
// Example:
// client := grok.NewClient()
// plan, err := client.Query(prompt) // or Generate, etc.
// Use the full todo content + a clean prompt asking only for "## Work Plan" section.
2026-03-31 19:56:45 +00:00
2026-03-31 20:04:40 +00:00
// Temporary placeholder (keeps it functional)
plan := "\n## Work Plan\n\n1. Review the todo/fix requirements.\n2. Implement the changes in the appropriate package.\n3. Add or update tests if applicable.\n4. Verify with `go test` and `grokkit lint`.\n5. Commit and push the branch.\n"
2026-03-31 19:56:45 +00:00
2026-03-31 19:42:32 +00:00
f , err := os . OpenFile ( path , os . O_APPEND | os . O_WRONLY , 0644 )
if err != nil {
return err
}
2026-03-31 19:56:45 +00:00
defer func ( f * os . File ) {
err := f . Close ( )
if err != nil {
2026-03-31 20:04:40 +00:00
2026-03-31 19:56:45 +00:00
}
} ( f )
2026-03-31 19:42:32 +00:00
_ , err = f . WriteString ( plan )
return err
}
2026-03-31 19:30:05 +00:00
2026-03-31 19:42:32 +00:00
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 ( )
}
2026-03-31 19:30:05 +00:00
2026-03-31 19:42:32 +00:00
func completeItem ( title string , isFix bool ) error {
2026-03-31 19:56:45 +00:00
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 )
}
2026-03-31 20:04:40 +00:00
// TODO: update todo/README.md index under ## Completed (for non-fixes)
2026-03-31 19:56:45 +00:00
if ! isFix {
2026-03-31 20:04:40 +00:00
logger . Info ( "TODO: update index README for completed todo" )
2026-03-31 19:56:45 +00:00
}
commitMsg := fmt . Sprintf ( "Complete work on %s" , title )
if err := commitChanges ( commitMsg ) ; err != nil {
return fmt . Errorf ( "complete commit failed: %w" , err )
}
2026-03-31 19:30:05 +00:00
return nil
}
2026-03-31 19:42:32 +00:00
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 ( ) {
2026-03-31 20:04:40 +00:00
// TODO: implement via config once internal/config or root config supports IDE command
// For now, silent graceful fallback per spec
2026-03-31 19:56:45 +00:00
logger . Debug ( "IDE open skipped (config support pending)" )
2026-03-31 19:42:32 +00:00
}