summaryrefslogtreecommitdiff
path: root/Omni/Jr.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Jr.hs')
-rwxr-xr-xOmni/Jr.hs155
1 files changed, 150 insertions, 5 deletions
diff --git a/Omni/Jr.hs b/Omni/Jr.hs
index 9e33ab5..69c395b 100755
--- a/Omni/Jr.hs
+++ b/Omni/Jr.hs
@@ -12,11 +12,14 @@
module Omni.Jr where
import Alpha
+import qualified Data.Aeson as Aeson
+import qualified Data.ByteString.Lazy.Char8 as BLC
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.Fact as Fact
import qualified Omni.Jr.Web as Web
import qualified Omni.Task as Task
import qualified Omni.Task.Core as TaskCore
@@ -52,6 +55,10 @@ Usage:
jr web [--port=PORT]
jr review [<task-id>] [--auto]
jr loop [--delay=SECONDS]
+ jr facts list [--project=PROJECT] [--json]
+ jr facts show <fact-id> [--json]
+ jr facts add <project> <content> [--files=FILES] [--task=TASK] [--confidence=CONF] [--json]
+ jr facts delete <fact-id> [--json]
jr test
jr (-h | --help)
@@ -61,12 +68,18 @@ Commands:
web Start the web UI server
review Review a completed task (show diff, accept/reject)
loop Run autonomous work+review loop
+ facts Manage knowledge base facts
Options:
- -h --help Show this help
- --port=PORT Port for web server [default: 8080]
- --auto Auto-review: accept if tests pass, reject if they fail
- --delay=SECONDS Delay between loop iterations [default: 5]
+ -h --help Show this help
+ --port=PORT Port for web server [default: 8080]
+ --auto Auto-review: accept if tests pass, reject if they fail
+ --delay=SECONDS Delay between loop iterations [default: 5]
+ --project=PROJECT Filter facts by project
+ --files=FILES Comma-separated list of related files
+ --task=TASK Source task ID
+ --confidence=CONF Confidence level 0.0-1.0 [default: 0.8]
+ --json Output in JSON format
|]
move :: Cli.Arguments -> IO ()
@@ -115,6 +128,7 @@ move args
Just d -> fromMaybe 5 (readMaybe d)
Nothing -> 5
runLoop delay
+ | args `Cli.has` Cli.command "facts" = handleFacts args
| otherwise = putText (str <| Docopt.usage help)
-- | Run the autonomous loop: work -> review -> repeat
@@ -507,6 +521,79 @@ checkEpicCompletion task =
where
hasParent pid t = maybe False (TaskCore.matchesId pid) (TaskCore.taskParent t)
+-- | Handle facts subcommands
+handleFacts :: Cli.Arguments -> IO ()
+handleFacts args
+ | args `Cli.has` Cli.command "list" = do
+ let maybeProject = Text.pack </ Cli.getArg args (Cli.longOption "project")
+ jsonMode = args `Cli.has` Cli.longOption "json"
+ facts <- maybe Fact.getAllFacts Fact.getFactsByProject maybeProject
+ if jsonMode
+ then BLC.putStrLn (Aeson.encode facts)
+ else traverse_ printFact facts
+ | args `Cli.has` Cli.command "show" = do
+ let jsonMode = args `Cli.has` Cli.longOption "json"
+ case Cli.getArg args (Cli.argument "fact-id") of
+ Nothing -> putText "fact-id required"
+ Just fidStr -> case readMaybe fidStr of
+ Nothing -> putText "Invalid fact ID (must be integer)"
+ Just fid -> do
+ maybeFact <- Fact.getFact fid
+ case maybeFact of
+ Nothing -> putText "Fact not found"
+ Just fact ->
+ if jsonMode
+ then BLC.putStrLn (Aeson.encode fact)
+ else printFactDetailed fact
+ | args `Cli.has` Cli.command "add" = do
+ let jsonMode = args `Cli.has` Cli.longOption "json"
+ case (Cli.getArg args (Cli.argument "project"), Cli.getArg args (Cli.argument "content")) of
+ (Just proj, Just content) -> do
+ let files = case Cli.getArg args (Cli.longOption "files") of
+ Just f -> Text.splitOn "," (Text.pack f)
+ Nothing -> []
+ sourceTask = Text.pack </ Cli.getArg args (Cli.longOption "task")
+ confidence = case Cli.getArg args (Cli.longOption "confidence") of
+ Just c -> fromMaybe 0.8 (readMaybe c)
+ Nothing -> 0.8
+ factId <- Fact.createFact (Text.pack proj) (Text.pack content) files sourceTask confidence
+ if jsonMode
+ then BLC.putStrLn (Aeson.encode (Aeson.object ["id" Aeson..= factId, "success" Aeson..= True]))
+ else putText ("Created fact: " <> tshow factId)
+ _ -> putText "project and content required"
+ | args `Cli.has` Cli.command "delete" = do
+ let jsonMode = args `Cli.has` Cli.longOption "json"
+ case Cli.getArg args (Cli.argument "fact-id") of
+ Nothing -> putText "fact-id required"
+ Just fidStr -> case readMaybe fidStr of
+ Nothing -> putText "Invalid fact ID (must be integer)"
+ Just fid -> do
+ Fact.deleteFact fid
+ if jsonMode
+ then BLC.putStrLn (Aeson.encode (Aeson.object ["success" Aeson..= True, "message" Aeson..= ("Deleted fact " <> tshow fid)]))
+ else putText ("Deleted fact: " <> tshow fid)
+ | otherwise = putText "Unknown facts subcommand. Use: list, show, add, or delete"
+
+-- | Print a fact in a compact format
+printFact :: TaskCore.Fact -> IO ()
+printFact fact = do
+ let fid = maybe "?" tshow (TaskCore.factId fact)
+ proj = TaskCore.factProject fact
+ content = Text.take 60 (TaskCore.factContent fact)
+ suffix = if Text.length (TaskCore.factContent fact) > 60 then "..." else ""
+ putText (fid <> "\t" <> proj <> "\t" <> content <> suffix)
+
+-- | Print a fact in detailed format
+printFactDetailed :: TaskCore.Fact -> IO ()
+printFactDetailed fact = do
+ putText ("ID: " <> maybe "?" tshow (TaskCore.factId fact))
+ putText ("Project: " <> TaskCore.factProject fact)
+ putText ("Content: " <> TaskCore.factContent fact)
+ putText ("Files: " <> Text.intercalate ", " (TaskCore.factRelatedFiles fact))
+ putText ("Source: " <> fromMaybe "-" (TaskCore.factSourceTask fact))
+ putText ("Confidence: " <> tshow (TaskCore.factConfidence fact))
+ putText ("Created: " <> tshow (TaskCore.factCreatedAt fact))
+
test :: Test.Tree
test =
Test.group
@@ -535,5 +622,63 @@ test =
Left err -> Test.assertFailure <| "Failed to parse 'work t-123': " <> show err
Right args -> do
args `Cli.has` Cli.command "work" Test.@?= True
- Cli.getArg args (Cli.argument "task-id") Test.@?= Just "t-123"
+ Cli.getArg args (Cli.argument "task-id") Test.@?= Just "t-123",
+ Test.unit "can parse facts list command" <| do
+ let result = Docopt.parseArgs help ["facts", "list"]
+ case result of
+ Left err -> Test.assertFailure <| "Failed to parse 'facts list': " <> show err
+ Right args -> do
+ args `Cli.has` Cli.command "facts" Test.@?= True
+ args `Cli.has` Cli.command "list" Test.@?= True,
+ Test.unit "can parse facts list with --project" <| do
+ let result = Docopt.parseArgs help ["facts", "list", "--project=myproj"]
+ case result of
+ Left err -> Test.assertFailure <| "Failed to parse 'facts list --project': " <> show err
+ Right args -> do
+ args `Cli.has` Cli.command "facts" Test.@?= True
+ args `Cli.has` Cli.command "list" Test.@?= True
+ Cli.getArg args (Cli.longOption "project") Test.@?= Just "myproj",
+ Test.unit "can parse facts list with --json" <| do
+ let result = Docopt.parseArgs help ["facts", "list", "--json"]
+ case result of
+ Left err -> Test.assertFailure <| "Failed to parse 'facts list --json': " <> show err
+ Right args -> do
+ args `Cli.has` Cli.command "facts" Test.@?= True
+ args `Cli.has` Cli.command "list" Test.@?= True
+ args `Cli.has` Cli.longOption "json" Test.@?= True,
+ Test.unit "can parse facts show command" <| do
+ let result = Docopt.parseArgs help ["facts", "show", "42"]
+ case result of
+ Left err -> Test.assertFailure <| "Failed to parse 'facts show 42': " <> show err
+ Right args -> do
+ args `Cli.has` Cli.command "facts" Test.@?= True
+ args `Cli.has` Cli.command "show" Test.@?= True
+ Cli.getArg args (Cli.argument "fact-id") Test.@?= Just "42",
+ Test.unit "can parse facts add command" <| do
+ let result = Docopt.parseArgs help ["facts", "add", "myproj", "This is a fact"]
+ case result of
+ Left err -> Test.assertFailure <| "Failed to parse 'facts add': " <> show err
+ Right args -> do
+ args `Cli.has` Cli.command "facts" Test.@?= True
+ args `Cli.has` Cli.command "add" Test.@?= True
+ Cli.getArg args (Cli.argument "project") Test.@?= Just "myproj"
+ Cli.getArg args (Cli.argument "content") Test.@?= Just "This is a fact",
+ Test.unit "can parse facts add with options" <| do
+ let result = Docopt.parseArgs help ["facts", "add", "myproj", "fact", "--files=a.hs,b.hs", "--task=t-123", "--confidence=0.9"]
+ case result of
+ Left err -> Test.assertFailure <| "Failed to parse 'facts add' with options: " <> show err
+ Right args -> do
+ args `Cli.has` Cli.command "facts" Test.@?= True
+ args `Cli.has` Cli.command "add" Test.@?= True
+ Cli.getArg args (Cli.longOption "files") Test.@?= Just "a.hs,b.hs"
+ Cli.getArg args (Cli.longOption "task") Test.@?= Just "t-123"
+ Cli.getArg args (Cli.longOption "confidence") Test.@?= Just "0.9",
+ Test.unit "can parse facts delete command" <| do
+ let result = Docopt.parseArgs help ["facts", "delete", "42"]
+ case result of
+ Left err -> Test.assertFailure <| "Failed to parse 'facts delete 42': " <> show err
+ Right args -> do
+ args `Cli.has` Cli.command "facts" Test.@?= True
+ args `Cli.has` Cli.command "delete" Test.@?= True
+ Cli.getArg args (Cli.argument "fact-id") Test.@?= Just "42"
]