Files
kandev/apps/backend/cmd/agentctl/kandev.go
T
2026-05-16 20:07:57 +01:00

126 lines
3.3 KiB
Go

package main
import (
"encoding/json"
"fmt"
"net/url"
"os"
)
// Shared subcommand literals so goconst doesn't flag duplicates across
// the per-noun handler files. `subcmdList` was added with the first
// CLI rollout; `subcmdCreate` was added when the office mutation
// surfaces (agents / routines / docs / task) landed; `subcmdGet` was
// added when the office costs CLI joined memory + task as `get`-style
// readers.
const (
subcmdList = "list"
subcmdCreate = "create"
subcmdGet = "get"
)
// runKandevCLI dispatches the kandev subcommand to the appropriate handler.
// Returns an exit code (0 = success, non-zero = error).
func runKandevCLI(args []string) int {
if len(args) == 0 {
printUsage()
return 1
}
switch args[0] {
case "task":
// Singular `task` group (get/update/create) stays for back
// compat with skills authored before the plural rollout.
// New skills should prefer `tasks` which covers list/move/
// archive/message/conversation as well.
return runTaskCmd(args[1:])
case "tasks":
return runTasksCmd(args[1:])
case "comment":
return runCommentCmd(args[1:])
case "agents":
return runAgentsCmd(args[1:])
case "memory":
return runMemoryCmd(args[1:])
case "checkout":
return runCheckoutCmd(args[1:])
case "label":
return runLabelCmd(args[1:])
case "doc":
return runDocCmd(args[1:])
case "routines":
return runRoutinesCmd(args[1:])
case "approvals":
return runApprovalsCmd(args[1:])
case "budget":
return runBudgetCmd(args[1:])
default:
cliError("unknown command: %s", args[0])
return 1
}
}
func printUsage() {
fmt.Fprintln(os.Stderr, "Usage: agentctl kandev <command> [flags]")
fmt.Fprintln(os.Stderr,
"Commands: task, tasks, comment, agents, memory, checkout, label, doc, routines, approvals, budget")
}
// cliError writes a JSON error object to stderr.
func cliError(format string, args ...any) {
msg := fmt.Sprintf(format, args...)
data, _ := json.Marshal(map[string]string{"error": msg})
fmt.Fprintln(os.Stderr, string(data))
}
// cliOutput writes JSON data to stdout.
func cliOutput(data []byte) {
_, _ = os.Stdout.Write(data)
// Ensure trailing newline for shell friendliness.
if len(data) == 0 || data[len(data)-1] != '\n' {
_, _ = os.Stdout.Write([]byte("\n"))
}
}
// handleResponse checks the HTTP status and writes output or error accordingly.
// Returns 0 on 2xx status, 1 otherwise.
func handleResponse(body []byte, status int, err error) int {
if err != nil {
cliError("%v", err)
return 1
}
if status >= 200 && status < 300 {
cliOutput(body)
return 0
}
// Non-2xx: write body to stderr as the error.
fmt.Fprintln(os.Stderr, string(body))
return 1
}
// getWithParams performs a GET request with query parameters. It handles
// client creation, required env var check, path building, and response output.
func getWithParams(basePath, requiredEnvName, requiredEnvVal string, params map[string]string) int {
client, err := newKandevClient()
if err != nil {
cliError("%v", err)
return 1
}
if requiredEnvVal == "" {
cliError("%s must be set", requiredEnvName)
return 1
}
values := url.Values{}
for k, v := range params {
if v == "" {
continue
}
values.Set(k, v)
}
q := ""
if encoded := values.Encode(); encoded != "" {
q = "?" + encoded
}
body, status, doErr := client.do("GET", basePath+q, nil)
return handleResponse(body, status, doErr)
}