]> git.giacomolli.uk - onechat/commitdiff
Separate commands from the entry point
authorAnderson A. Giacomolli <redacted>
Wed, 11 Nov 2026 13:33:16 +0000 (10:33 -0300)
committerAnderson A. Giacomolli <redacted>
Wed, 11 Nov 2026 13:33:16 +0000 (10:33 -0300)
Command parsing and dispatch have their own source file beside their
tests. Help text, build version, output, and returned errors stay the
same. The entry point keeps run, which creates and cancels the signal
context before it calls the command parser.

Issue: 947bddb

command.go [new file with mode: 0644]
main.go

diff --git a/command.go b/command.go
new file mode 100644 (file)
index 0000000..a49f1d9
--- /dev/null
@@ -0,0 +1,76 @@
+package main
+
+import (
+       "context"
+       "fmt"
+       "os"
+       "strings"
+)
+
+// usage is the help text of the command.
+const usage = `Usage: onechat [COMMAND]
+
+Commands:
+  backup FILE              archive the running daemon data
+  restore FILE             restore into an empty data directory
+  chat                     talk to the agent in this terminal (default)
+  daemon                   run the agent, Telegram, and WhatsApp
+  import [-n] [--agent NAME] ZIP...
+                           import WhatsApp history (stop the daemon first)
+    -n                     print the plan without model calls
+    --agent NAME           import into an existing direct child (default: root)
+  login                    log in to ChatGPT with a device code
+  probe LOG COUNT [BUILDS] measure the compactor on a log, with real calls
+  version                  show the build version
+  help                     show this help
+
+Chat needs a running daemon. ONECHAT_DIR sets the data directory.
+In chat, /image PATH [caption] sends a JPEG, PNG, or WebP image up to 20 MB.
+Quote PATH with single or double quotes when it contains spaces.
+`
+
+// version is set by the package build and stays dev for direct Go builds.
+var version = "dev"
+
+// parseCommand dispatches args with ctx and prints command output.
+func parseCommand(ctx context.Context, args []string) error {
+       switch {
+       case len(args) == 1 && (args[0] == "help" || args[0] == "--help" || args[0] == "-h"):
+               _, err := fmt.Print(usage)
+               if err != nil {
+                       return fmt.Errorf("write command help: %w", err)
+               }
+       case len(args) == 1 && args[0] == "version":
+               _, err := fmt.Println(version)
+               if err != nil {
+                       return fmt.Errorf("write build version: %w", err)
+               }
+       case len(args) == 1 && args[0] == "login":
+               err := chatgptLogin(ctx, os.Stdout)
+               if err != nil {
+                       return err
+               }
+
+       // A signal is the normal stop of the daemon and the client, so their
+       // result does not include ctx.Err().
+       case len(args) == 1 && args[0] == "daemon":
+               return daemon(ctx, os.Stdout)
+       case len(args) == 0 || len(args) == 1 && args[0] == "chat":
+               return terminal(ctx, os.Stdin, os.Stdout)
+       case len(args) == 2 && args[0] == "backup":
+               return backup(ctx, args[1], os.Stdout)
+       case len(args) == 2 && args[0] == "restore":
+               return restore(ctx, args[1], os.Stdout)
+       case len(args) > 0 && args[0] == "import":
+               return whatsappImport(ctx, args[1:], os.Stdout)
+       case len(args) > 0 && args[0] == "probe":
+               err := probe(ctx, args[1:], os.Stdout)
+               if err != nil {
+                       return err
+               }
+       default:
+               return fmt.Errorf("unknown command %q\n\n%s", strings.Join(args, " "), strings.TrimSuffix(usage, "\n"))
+       }
+
+       return ctx.Err()
+}
diff --git a/main.go b/main.go
index 10ac995a9555b2885070e2d8baf77a1eed30fa4f..400538a39a3193be98a7f54d3060ce459a9bc970 100644 (file)
--- a/main.go
+++ b/main.go
@@ -6,35 +6,9 @@ import (
        "fmt"
        "os"
        "os/signal"
-       "strings"
        "syscall"
 )
 
-// usage is the help text of the command.
-const usage = `Usage: onechat [COMMAND]
-
-Commands:
-  backup FILE              archive the running daemon data
-  restore FILE             restore into an empty data directory
-  chat                     talk to the agent in this terminal (default)
-  daemon                   run the agent, Telegram, and WhatsApp
-  import [-n] [--agent NAME] ZIP...
-                           import WhatsApp history (stop the daemon first)
-    -n                     print the plan without model calls
-    --agent NAME           import into an existing direct child (default: root)
-  login                    log in to ChatGPT with a device code
-  probe LOG COUNT [BUILDS] measure the compactor on a log, with real calls
-  version                  show the build version
-  help                     show this help
-
-Chat needs a running daemon. ONECHAT_DIR sets the data directory.
-In chat, /image PATH [caption] sends a JPEG, PNG, or WebP image up to 20 MB.
-Quote PATH with single or double quotes when it contains spaces.
-`
-
-// version is set by the package build and stays dev for direct Go builds.
-var version = "dev"
-
 func main() {
        err := run()
        if err != nil {
@@ -47,44 +21,5 @@ func run() error {
        ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
        defer cancel()
 
-       args := os.Args[1:]
-       switch {
-       case len(args) == 1 && (args[0] == "help" || args[0] == "--help" || args[0] == "-h"):
-               _, err := fmt.Print(usage)
-               if err != nil {
-                       return fmt.Errorf("write command help: %w", err)
-               }
-       case len(args) == 1 && args[0] == "version":
-               _, err := fmt.Println(version)
-               if err != nil {
-                       return fmt.Errorf("write build version: %w", err)
-               }
-       case len(args) == 1 && args[0] == "login":
-               err := chatgptLogin(ctx, os.Stdout)
-               if err != nil {
-                       return err
-               }
-
-       // A signal is the normal stop of the daemon and the client, so their
-       // result does not include ctx.Err().
-       case len(args) == 1 && args[0] == "daemon":
-               return daemon(ctx, os.Stdout)
-       case len(args) == 0 || len(args) == 1 && args[0] == "chat":
-               return terminal(ctx, os.Stdin, os.Stdout)
-       case len(args) == 2 && args[0] == "backup":
-               return backup(ctx, args[1], os.Stdout)
-       case len(args) == 2 && args[0] == "restore":
-               return restore(ctx, args[1], os.Stdout)
-       case len(args) > 0 && args[0] == "import":
-               return whatsappImport(ctx, args[1:], os.Stdout)
-       case len(args) > 0 && args[0] == "probe":
-               err := probe(ctx, args[1:], os.Stdout)
-               if err != nil {
-                       return err
-               }
-       default:
-               return fmt.Errorf("unknown command %q\n\n%s", strings.Join(args, " "), strings.TrimSuffix(usage, "\n"))
-       }
-
-       return ctx.Err()
+       return parseCommand(ctx, os.Args[1:])
 }