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

184 lines
4.4 KiB
Go

package main
import (
"flag"
"fmt"
"io"
"mime"
"net/http"
"os"
"path/filepath"
)
func runDocCmd(args []string) int {
if len(args) == 0 {
cliError("usage: agentctl kandev doc <create|read|list|upload> [flags]")
return 1
}
switch args[0] {
case subcmdCreate:
return docCreate(args[1:])
case "read":
return docRead(args[1:])
case subcmdList:
return docList(args[1:])
case "upload":
return docUpload(args[1:])
default:
cliError("unknown doc subcommand: %s", args[0])
return 1
}
}
// docCreate creates or updates a document for a task.
// Usage: kandev doc create <task-id> <key> --type <type> --title <title> --content <content>
func docCreate(args []string) int {
// Go's flag.FlagSet.Parse stops at the first non-flag argument, so the
// documented "positional THEN flags" form would silently leave --type /
// --title / --content at their defaults. Peel off the two required
// positionals first and parse the remainder as flags.
if len(args) < 2 {
cliError("usage: kandev doc create <task-id> <key> [flags]")
return 1
}
taskID := args[0]
key := args[1]
fs := flag.NewFlagSet("doc create", flag.ContinueOnError)
docType := fs.String("type", "custom", "Document type (plan, spec, notes, review, attachment, custom)")
title := fs.String("title", "", "Document title")
content := fs.String("content", "", "Document content (markdown). If empty, reads from stdin.")
if err := fs.Parse(args[2:]); err != nil {
cliError("parse flags: %v", err)
return 1
}
docContent := *content
if docContent == "" {
data, err := io.ReadAll(os.Stdin)
if err != nil {
cliError("read stdin: %v", err)
return 1
}
docContent = string(data)
}
client, err := newKandevClient()
if err != nil {
cliError("%v", err)
return 1
}
payload := map[string]interface{}{
"type": *docType,
"title": *title,
"content": docContent,
}
body, status, doErr := client.do(http.MethodPut,
fmt.Sprintf("/api/v1/tasks/%s/documents/%s", taskID, key), payload)
return handleResponse(body, status, doErr)
}
// docRead fetches and prints the content of a document.
// Usage: kandev doc read <task-id> <key>
func docRead(args []string) int {
fs := flag.NewFlagSet("doc read", flag.ContinueOnError)
if err := fs.Parse(args); err != nil {
cliError("parse flags: %v", err)
return 1
}
positional := fs.Args()
if len(positional) < 2 {
cliError("usage: kandev doc read <task-id> <key>")
return 1
}
taskID := positional[0]
key := positional[1]
client, err := newKandevClient()
if err != nil {
cliError("%v", err)
return 1
}
body, status, doErr := client.do(http.MethodGet,
fmt.Sprintf("/api/v1/tasks/%s/documents/%s", taskID, key), nil)
return handleResponse(body, status, doErr)
}
// docList lists all documents for a task.
// Usage: kandev doc list <task-id>
func docList(args []string) int {
fs := flag.NewFlagSet("doc list", flag.ContinueOnError)
if err := fs.Parse(args); err != nil {
cliError("parse flags: %v", err)
return 1
}
positional := fs.Args()
if len(positional) < 1 {
cliError("usage: kandev doc list <task-id>")
return 1
}
taskID := positional[0]
client, err := newKandevClient()
if err != nil {
cliError("%v", err)
return 1
}
body, status, doErr := client.do(http.MethodGet,
fmt.Sprintf("/api/v1/tasks/%s/documents", taskID), nil)
return handleResponse(body, status, doErr)
}
// docUpload uploads a file as an attachment document.
// Usage: kandev doc upload <task-id> <key> <filepath>
func docUpload(args []string) int {
fs := flag.NewFlagSet("doc upload", flag.ContinueOnError)
if err := fs.Parse(args); err != nil {
cliError("parse flags: %v", err)
return 1
}
positional := fs.Args()
if len(positional) < 3 {
cliError("usage: kandev doc upload <task-id> <key> <filepath>")
return 1
}
taskID := positional[0]
key := positional[1]
filePath := positional[2]
data, err := os.ReadFile(filePath)
if err != nil {
cliError("read file %q: %v", filePath, err)
return 1
}
filename := filepath.Base(filePath)
mimeType := mime.TypeByExtension(filepath.Ext(filename))
if mimeType == "" {
mimeType = "application/octet-stream"
}
client, err := newKandevClient()
if err != nil {
cliError("%v", err)
return 1
}
payload := map[string]interface{}{
"filename": filename,
"mime_type": mimeType,
"data": data,
}
body, status, doErr := client.do(http.MethodPost,
fmt.Sprintf("/api/v1/tasks/%s/documents/%s/upload", taskID, key), payload)
return handleResponse(body, status, doErr)
}