← Back to task

Commit bc7117f5

commit bc7117f55b9bed66cdcc1bd84a34d38ec7eb4edd
Author: Ben Sima <ben@bensima.com>
Date:   Tue Nov 25 22:28:47 2025

    jr: add review command, --try-opus, Task-Id trailer
    
    - jr review <task-id>: show diff, accept/reject/skip - Worker uses
    --try-opus for better code quality - Commit messages use Task-Id:
    trailer (Gerrit-style)
    
    Task-Id: t-1o2g8gu6p8o

diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs
index fe9194f3..eec0cf78 100644
--- a/Omni/Agent/Worker.hs
+++ b/Omni/Agent/Worker.hs
@@ -68,8 +68,8 @@ processTask worker task = do
       -- Update status to Review
       TaskCore.updateTaskStatus tid TaskCore.Review []
 
-      -- Commit changes using Amp output
-      let commitMsg = "feat: implement " <> tid <> "\n\n" <> output
+      -- Commit changes using Amp output (Gerrit-style trailer)
+      let commitMsg = Text.strip output <> "\n\nTask-Id: " <> tid
       Git.commit repo commitMsg
 
       -- Submit for review
@@ -125,7 +125,7 @@ runAmp repo task = do
   Directory.createDirectoryIfMissing True (repo </> "_/llm")
 
   -- Assume amp is in PATH
-  let args = ["--log-level", "debug", "--log-file", "_/llm/amp.log", "--dangerously-allow-all", "-x", Text.unpack fullPrompt]
+  let args = ["--try-opus", "--log-level", "debug", "--log-file", "_/llm/amp.log", "--dangerously-allow-all", "-x", Text.unpack fullPrompt]
 
   let cp = (Process.proc "amp" args) {Process.cwd = Just repo, Process.std_out = Process.CreatePipe}
   (_, Just hOut, _, ph) <- Process.createProcess cp
diff --git a/Omni/Jr.hs b/Omni/Jr.hs
index 32322d2b..56cabd18 100644
--- a/Omni/Jr.hs
+++ b/Omni/Jr.hs
@@ -7,11 +7,13 @@
 module Omni.Jr where
 
 import Alpha
+import qualified Data.List as List
 import qualified Data.Text as Text
 import qualified Omni.Agent.Core as AgentCore
 import qualified Omni.Agent.Worker as AgentWorker
 import qualified Omni.Cli as Cli
 import qualified Omni.Task as Task
+import qualified Omni.Task.Core as TaskCore
 import qualified Omni.Test as Test
 import qualified System.Console.Docopt as Docopt
 import qualified System.Directory as Directory
@@ -19,6 +21,8 @@ import System.Environment (withArgs)
 import qualified System.Environment as Env
 import qualified System.Exit as Exit
 import System.FilePath (takeFileName)
+import qualified System.IO as IO
+import qualified System.Process as Process
 
 main :: IO ()
 main = Cli.main plan
@@ -40,6 +44,7 @@ jr
 Usage:
   jr task [<args>...]
   jr work [<task-id>]
+  jr review <task-id>
   jr merge-driver <ours> <theirs>
   jr test
   jr (-h | --help)
@@ -47,6 +52,7 @@ Usage:
 Commands:
   task          Manage tasks
   work          Start a worker agent on a task
+  review        Review a completed task (show diff, accept/reject)
   merge-driver  Internal git merge driver
 
 Options:
@@ -77,6 +83,9 @@ move args
       let taskId = fmap Text.pack (Cli.getArg args (Cli.argument "task-id"))
 
       AgentWorker.start worker taskId
+  | args `Cli.has` Cli.command "review" = do
+      tidStr <- getArgOrExit args (Cli.argument "task-id")
+      reviewTask (Text.pack tidStr)
   | args `Cli.has` Cli.command "merge-driver" = mergeDriver args
   | otherwise = putText (str <| Docopt.usage help)
 
@@ -108,6 +117,55 @@ getArgOrExit args opt =
       putText <| "Error: Missing required argument " <> Text.pack (show opt)
       Exit.exitFailure
 
+reviewTask :: Text -> IO ()
+reviewTask tid = do
+  tasks <- TaskCore.loadTasks
+  case TaskCore.findTask tid tasks of
+    Nothing -> do
+      putText ("Task " <> tid <> " not found.")
+      Exit.exitFailure
+    Just task -> do
+      TaskCore.showTaskDetailed task
+
+      let grepArg = "--grep=" <> Text.unpack tid
+      (code, shaOut, _) <-
+        Process.readProcessWithExitCode
+          "git"
+          ["log", "--pretty=format:%H", "-n", "1", grepArg]
+          ""
+
+      when (code /= Exit.ExitSuccess || null shaOut) <| do
+        putText "\nNo commit found for this task."
+        putText "The worker may not have completed yet, or the commit message doesn't include the task ID."
+        Exit.exitFailure
+
+      let commitSha = case List.lines shaOut of
+            (x : _) -> x
+            [] -> ""
+
+      putText "\n=== Diff for this task ===\n"
+      _ <- Process.rawSystem "git" ["show", "--stat", commitSha]
+      putText ""
+      _ <- Process.rawSystem "git" ["show", "--no-stat", commitSha]
+
+      putText "\n[a]ccept / [r]eject / [s]kip? "
+      IO.hFlush IO.stdout
+      choice <- getLine
+
+      case Text.toLower choice of
+        c
+          | "a" `Text.isPrefixOf` c -> do
+              TaskCore.updateTaskStatus tid TaskCore.Done []
+              putText ("Task " <> tid <> " marked as Done.")
+          | "r" `Text.isPrefixOf` c -> do
+              putText "Enter rejection reason: "
+              IO.hFlush IO.stdout
+              reason <- getLine
+              TaskCore.updateTaskStatus tid TaskCore.Open []
+              putText ("Task " <> tid <> " reopened.")
+              putText ("Reason: " <> reason)
+          | otherwise -> putText "Skipped; no status change."
+
 test :: Test.Tree
 test =
   Test.group