packages feed

kioku-cli 0.6.0.0 → 0.7.0.0

raw patch · 7 files changed

+230/−115 lines, 7 filesdep ~keirodep ~kioku-apidep ~kioku-cliPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: keiro, kioku-api, kioku-cli, kioku-core, kioku-migrations, optparse-applicative

API changes (from Hackage documentation)

+ Kioku.Cli: cliParserInfo :: ParserInfo Command
+ Kioku.Cli: cliParserPrefs :: ParserPrefs

Files

CHANGELOG.md view
@@ -1,5 +1,15 @@ # Changelog +## 0.7.0.0 — 2026-09-18++### Changed++- Requires the Kioku 0.7 cohort and optparse-applicative `>=0.19 && <0.20`.+- Help renders at a stable 100-column width and groups recall targets, query/output controls, AI+  configuration, and worker execution modes under semantic headings. Command names, option+  spellings, defaults, and errors are unchanged.+- Bash, zsh, and fish completion-script requests are covered as public parser behavior.+ ## 0.6.0.0 — 2026-09-08  ### Breaking Changes
kioku-cli.cabal view
@@ -1,35 +1,40 @@-cabal-version:   3.0-name:            kioku-cli-version:         0.6.0.0-synopsis:        kioku command-line interface+cabal-version: 3.0+name: kioku-cli+version: 0.7.0.0+synopsis: kioku command-line interface description:   Command-line entry point for kioku demos and operational commands. -license:         BSD-3-Clause-license-file:    LICENSE-author:          Nadeem Bitar-maintainer:      nadeem@gmail.com-copyright:       2026 Nadeem Bitar-category:        Data-build-type:      Simple-tested-with:     GHC >=9.12 && <9.13-homepage:        https://github.com/shinzui/kioku-bug-reports:     https://github.com/shinzui/kioku/issues+license: BSD-3-Clause+license-file: LICENSE+author: Nadeem Bitar+maintainer: nadeem@gmail.com+copyright: 2026 Nadeem Bitar+category: Data+build-type: Simple+tested-with: ghc >=9.12 && <9.13+homepage: https://github.com/shinzui/kioku+bug-reports: https://github.com/shinzui/kioku/issues extra-doc-files: CHANGELOG.md  source-repository head-  type:     git+  type: git   location: https://github.com/shinzui/kioku.git-  subdir:   kioku-cli+  subdir: kioku-cli  common warnings   ghc-options:-    -Wall -Wcompat -Widentities -Wincomplete-record-updates-    -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints+    -Wall+    -Wcompat+    -Widentities+    -Wincomplete-record-updates+    -Wincomplete-uni-patterns+    -Wpartial-fields+    -Wredundant-constraints     -Werror=incomplete-patterns  common shared-  default-language:   GHC2024+  default-language: GHC2024   default-extensions:     BlockArguments     DeriveAnyClass@@ -42,8 +47,8 @@     TemplateHaskell  library-  import:           warnings, shared-  hs-source-dirs:   src+  import: warnings, shared+  hs-source-dirs: src   exposed-modules:     Kioku.Cli     Kioku.Cli.AIConfig@@ -72,61 +77,68 @@   -- mori://MMZK1526/mmzk-typeid/upstream-issues/mmzk-typeid-kindid-ghc-9-12-4-profiling-coercionkind-panic   ghc-prof-options: -fno-opt-coercion   build-depends:-    , async                 >=2.2       && <2.3-    , baikai                ^>=0.7.0.0-    , base                  >=4.21      && <5-    , containers            >=0.6       && <0.8-    , directory             >=1.3       && <1.4-    , effectful             >=2.5       && <2.7-    , keiro                 ^>=0.16.0.0-    , kioku-api             ^>=0.6.0.0-    , kioku-core            ^>=0.6.0.0-    , kiroku-store          ^>=0.8.0.0-    , optparse-applicative  >=0.18      && <0.20-    , text                  >=2.1       && <2.2-    , time                  >=1.12      && <1.15-    , uuid                  >=1.3       && <1.4+    async >=2.2 && <2.3,+    baikai ^>=0.7.0.0,+    base >=4.21 && <5,+    containers >=0.6 && <0.8,+    directory >=1.3 && <1.4,+    effectful >=2.5 && <2.7,+    keiro ^>=0.17.0.0,+    kioku-api ^>=0.7.0.0,+    kioku-core ^>=0.7.0.0,+    kiroku-store ^>=0.8.0.0,+    optparse-applicative >=0.19 && <0.20,+    text >=2.1 && <2.2,+    time >=1.12 && <1.15,+    uuid >=1.3 && <1.4,  executable kioku-  import:         warnings, shared-  main-is:        Main.hs+  import: warnings, shared+  main-is: Main.hs   hs-source-dirs: app-  ghc-options:    -threaded -rtsopts -with-rtsopts=-N+  ghc-options:+    -threaded+    -rtsopts+    -with-rtsopts=-N+   build-depends:-    , base       >=4.21     && <5-    , kioku-cli  ^>=0.6.0.0+    base >=4.21 && <5,+    kioku-cli ^>=0.7.0.0,  test-suite kioku-cli-test-  import:             warnings, shared-  type:               exitcode-stdio-1.0-  main-is:            Main.hs-  hs-source-dirs:     test+  import: warnings, shared+  type: exitcode-stdio-1.0+  main-is: Main.hs+  hs-source-dirs: test   other-modules:     Kioku.Cli.ParserSpec     Kioku.Cli.RecallEndToEndSpec -  ghc-options:        -threaded -rtsopts -with-rtsopts=-N+  ghc-options:+    -threaded+    -rtsopts+    -with-rtsopts=-N    -- Kioku.Cli.RecallEndToEndSpec runs the real binary as a subprocess: stdout, stderr and the   -- environment are process-wide, and tasty runs cases concurrently, so redirecting them in   -- process would race the rest of the suite.   build-tool-depends: kioku-cli:kioku   build-depends:-    , aeson                          >=2.2       && <2.3-    , base                           >=4.21      && <5-    , effectful                      >=2.5       && <2.7-    , filepath                       >=1.4       && <1.6-    , keiro                          ^>=0.16.0.0-    , kioku-api                      ^>=0.6.0.0-    , kioku-cli                      ^>=0.6.0.0-    , kioku-core                     ^>=0.6.0.0-    , kioku-migrations:test-support  ^>=0.6.0.0-    , kiroku-store                   ^>=0.8.0.0-    , optparse-applicative           >=0.18-    , process                        >=1.6       && <1.7-    , tasty                          >=1.5-    , tasty-hunit                    >=0.10-    , temporary                      >=1.3-    , text                           >=2.1-    , time                           >=1.12      && <1.15-    , uuid                           >=1.3       && <1.4+    aeson >=2.2 && <2.3,+    base >=4.21 && <5,+    effectful >=2.5 && <2.7,+    filepath >=1.4 && <1.6,+    keiro ^>=0.17.0.0,+    kioku-api ^>=0.7.0.0,+    kioku-cli ^>=0.7.0.0,+    kioku-core ^>=0.7.0.0,+    kioku-migrations:test-support ^>=0.7.0.0,+    kiroku-store ^>=0.8.0.0,+    optparse-applicative >=0.19 && <0.20,+    process >=1.6 && <1.7,+    tasty >=1.5,+    tasty-hunit >=0.10,+    temporary >=1.3,+    text >=2.1,+    time >=1.12 && <1.15,+    uuid >=1.3 && <1.4,
src/Kioku/Cli.hs view
@@ -1,5 +1,7 @@ module Kioku.Cli-  ( main,+  ( cliParserInfo,+    cliParserPrefs,+    main,   ) where @@ -16,15 +18,21 @@ data Command = Artifacts ArtifactsOptions | Demo DemoOptions | DemoSession DemoSessionOptions | Distill DistillOptions | Persona PersonaOptions | Recall RecallOptions | Scenes ScenesOptions | Worker WorkerOptions  main :: IO ()-main = run =<< execParser opts-  where-    opts =-      info-        (commandParser <**> helper)-        ( fullDesc-            <> progDesc "kioku reusable agent memory tools"-            <> header "kioku"-        )+main = run =<< customExecParser cliParserPrefs cliParserInfo++-- | A fixed width makes help output stable in terminals, tests, and generated+-- documentation instead of inheriting the invoking process's console width.+cliParserPrefs :: ParserPrefs+cliParserPrefs = prefs (columns 100)++cliParserInfo :: ParserInfo Command+cliParserInfo =+  info+    (commandParser <**> helper)+    ( fullDesc+        <> progDesc "kioku reusable agent memory tools"+        <> header "kioku"+    )  commandParser :: Parser Command commandParser =
src/Kioku/Cli/Commands/Distill.hs view
@@ -49,25 +49,32 @@     <$> argument       (eitherReader parseSessionId)       (metavar "SESSION_ID")-    <*> option-      (eitherReader parseCandidateSource)-      ( long "candidates"-          <> metavar "scan|recall"-          <> value CandidateScan-          <> help "Candidate lookup source"-      )-    <*> option-      (boundedIntReader "LIMIT" 1 50)-      ( long "limit"-          <> metavar "N"-          <> value 5-          <> help "Maximum merge candidates per extracted atom (1-50)"-      )-    <*> switch-      ( long "force"-          <> help "Re-run even when the session has no turns newer than the last successful pass"-      )-    <*> aiConfigOption+    <*> parserOptionGroup "Candidate options" candidateSourceParser+    <*> parserOptionGroup "Candidate options" candidateLimitParser+    <*> parserOptionGroup "Execution options" forceParser+    <*> parserOptionGroup "AI options" aiConfigOption+  where+    candidateSourceParser =+      option+        (eitherReader parseCandidateSource)+        ( long "candidates"+            <> metavar "scan|recall"+            <> value CandidateScan+            <> help "Candidate lookup source"+        )+    candidateLimitParser =+      option+        (boundedIntReader "LIMIT" 1 50)+        ( long "limit"+            <> metavar "N"+            <> value 5+            <> help "Maximum merge candidates per extracted atom (1-50)"+        )+    forceParser =+      switch+        ( long "force"+            <> help "Re-run even when the session has no turns newer than the last successful pass"+        )  runDistill :: DistillOptions -> IO () runDistill opts = do
src/Kioku/Cli/Commands/Recall.hs view
@@ -72,26 +72,33 @@ recallOptionsParser =   RecallOptions     <$> (Text.pack <$> argument str (metavar "QUERY"))-    <*> recallTargetParser-    <*> option-      (eitherReader (first Text.unpack . parseRecallStrategy . Text.pack))-      ( long "strategy"-          <> metavar strategyMetavar-          <> value Hybrid-          <> help "Recall strategy"-      )-    <*> option-      (boundedIntReader "LIMIT" 1 100)-      ( long "limit"-          <> metavar "N"-          <> value 8-          <> help "Maximum hits to return (1-100)"-      )-    <*> switch-      ( long "show-scores"-          <> help "Print fused scores and component ranks"-      )-    <*> aiConfigOption+    <*> parserOptionGroup "Recall target" recallTargetParser+    <*> parserOptionGroup "Query options" strategyParser+    <*> parserOptionGroup "Query options" limitParser+    <*> parserOptionGroup "Output options" showScoresParser+    <*> parserOptionGroup "AI options" aiConfigOption+  where+    strategyParser =+      option+        (eitherReader (first Text.unpack . parseRecallStrategy . Text.pack))+        ( long "strategy"+            <> metavar strategyMetavar+            <> value Hybrid+            <> help "Recall strategy"+        )+    limitParser =+      option+        (boundedIntReader "LIMIT" 1 100)+        ( long "limit"+            <> metavar "N"+            <> value 8+            <> help "Maximum hits to return (1-100)"+        )+    showScoresParser =+      switch+        ( long "show-scores"+            <> help "Print fused scores and component ranks"+        )  -- | Exactly one of the three target flags, and never two. --
src/Kioku/Cli/Commands/Worker.hs view
@@ -61,7 +61,10 @@   deriving stock (Eq, Show)  workerOptionsParser :: Parser WorkerOptions-workerOptionsParser = (\config mode -> maybe mode (`WorkerConfigured` mode) config) <$> aiConfigOption <*> workerModeParser+workerOptionsParser =+  (\config mode -> maybe mode (`WorkerConfigured` mode) config)+    <$> parserOptionGroup "AI options" aiConfigOption+    <*> parserOptionGroup "Execution mode" workerModeParser  workerModeParser :: Parser WorkerOptions workerModeParser =
test/Kioku/Cli/ParserSpec.hs view
@@ -10,6 +10,7 @@ import Keiro.Timer (TimerId (..)) import Kioku.Api.Access (mkMemorySpaceId) import Kioku.Api.Scope (MemoryScope (..), Namespace (..), ScopeKind (..))+import Kioku.Cli (cliParserInfo, cliParserPrefs) import Kioku.Cli.Commands.Demo (DemoOptions (..), demoOptionsParser, demoScope) import Kioku.Cli.Commands.DemoSession (DemoSessionOptions (..), demoSessionOptionsParser) import Kioku.Cli.Commands.Distill (DistillOptions (..), distillOptionsParser)@@ -27,6 +28,7 @@     recallStrategyText,   ) import Options.Applicative+import System.Exit (ExitCode (..)) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (assertBool, testCase, (@?=)) @@ -41,7 +43,8 @@       limitTests,       demoGuardTests,       redactionTests,-      workerModeTests+      workerModeTests,+      cliContractTests     ]  -- | Run a parser against an argument list, rendering a failure the way the real CLI would.@@ -157,7 +160,7 @@           Left err -> do             assertBool ("failure should say what is missing: " <> err) ("Missing:" `isInfixOf` err)             mapM_-              (\flag -> assertBool ("failure should list " <> flag <> ": " <> err) (flag `isInfixOf` err))+              (\flagName -> assertBool ("failure should list " <> flagName <> ": " <> err) (flagName `isInfixOf` err))               ["--scope", "--global-bucket", "--namespace-wide"],       testCase "two targets is a parse error naming the second" do         assertConflict "--namespace-wide" (target ["--scope", "a:b:c", "--namespace-wide", "mori"]),@@ -347,3 +350,68 @@      spaceNamed raw =       either (error . Text.unpack) id (mkMemorySpaceId (Text.pack raw))++-- | Help and completion are public CLI output even though the parser values are+-- pure. Keep their structure stable without pinning incidental whitespace.+cliContractTests :: TestTree+cliContractTests =+  testGroup+    "top-level help and completion contract"+    [ testCase "top-level help lists every command at the stable width" do+        rendered <- renderHelp ["--help"]+        mapM_+          (\commandName -> assertBool ("help should list " <> commandName) (commandName `isInfixOf` rendered))+          [ "demo",+            "demo-session",+            "distill",+            "migrate-artifacts",+            "persona",+            "recall",+            "scenes",+            "worker"+          ]+        assertStableWidth rendered,+      testCase "worker help exposes semantic option groups" do+        rendered <- renderHelp ["worker", "--help"]+        assertBool ("worker help should show the AI group:\n" <> rendered) ("AI options" `isInfixOf` rendered)+        assertBool ("worker help should show the execution group:\n" <> rendered) ("Execution mode" `isInfixOf` rendered)+        assertStableWidth rendered,+      testCase "recall help exposes target, query, and output groups" do+        rendered <- renderHelp ["recall", "--help"]+        mapM_+          (\heading -> assertBool ("recall help should show " <> heading <> ":\n" <> rendered) (heading `isInfixOf` rendered))+          ["Recall target", "Query options", "Output options", "AI options"]+        assertStableWidth rendered,+      testCase "bash completion script is generated" do+        script <- renderCompletion "--bash-completion-script"+        assertBool "bash completion script should name kioku" (not (null script) && "kioku" `isInfixOf` script),+      testCase "zsh completion script is generated" do+        script <- renderCompletion "--zsh-completion-script"+        assertBool "zsh completion script should name kioku" (not (null script) && "kioku" `isInfixOf` script),+      testCase "fish completion script is generated" do+        script <- renderCompletion "--fish-completion-script"+        assertBool "fish completion script should name kioku" (not (null script) && "kioku" `isInfixOf` script)+    ]+  where+    renderHelp args =+      case execParserPure cliParserPrefs cliParserInfo args of+        Failure failure -> do+          let (rendered, exitCode) = renderFailure failure "kioku"+          exitCode @?= ExitSuccess+          pure rendered+        Success _ -> assertBool "--help unexpectedly parsed as a command" False >> pure ""+        CompletionInvoked _ -> assertBool "--help unexpectedly invoked completion" False >> pure ""++    renderCompletion flagName =+      case execParserPure cliParserPrefs cliParserInfo [flagName, "kioku"] of+        CompletionInvoked completion -> execCompletion completion "kioku"+        Failure failure -> do+          let (rendered, _) = renderFailure failure "kioku"+          assertBool (flagName <> " failed:\n" <> rendered) False+          pure ""+        Success _ -> assertBool (flagName <> " unexpectedly parsed as a command") False >> pure ""++    assertStableWidth rendered =+      mapM_+        (\line -> assertBool ("help line exceeds 100 columns: " <> show line) (length line <= 100))+        (lines rendered)