afbe270181
Two changes: 1. structured-add-commands: The implicit note shorthand (kb "text") caused accidental note creation from mistyped commands. Replaced with explicit kb addnote <text> command. Root command reverts to standard Cobra behaviour. Updated examples, tests, SKILL.md, and specs. 2. split-readme-developer-docs: Moved build-from-source instructions, release process, API reference, and ROCm migration notes from README.md into a new DEVELOPER.md. README now links to DEVELOPER.md for dev workflows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
89 lines
2.1 KiB
Go
89 lines
2.1 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
|
|
"github.com/kb-search/kb/internal/api"
|
|
"github.com/kb-search/kb/internal/output"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var addnoteCmd = &cobra.Command{
|
|
Use: "addnote <text>",
|
|
Short: "Add a text note to the knowledge base",
|
|
Args: func(cmd *cobra.Command, args []string) error {
|
|
if len(args) == 0 {
|
|
return fmt.Errorf("requires a note text argument\n\n Usage: kb addnote \"your note text here\"")
|
|
}
|
|
if len(args) > 1 {
|
|
return fmt.Errorf("accepts 1 arg but received %d — quote your note text, e.g. kb addnote \"your note text here\"", len(args))
|
|
}
|
|
return nil
|
|
},
|
|
RunE: runAddnote,
|
|
}
|
|
|
|
func init() {
|
|
addnoteCmd.Flags().String("tags", "", "tags (comma-separated)")
|
|
rootCmd.AddCommand(addnoteCmd)
|
|
}
|
|
|
|
func runAddnote(cmd *cobra.Command, args []string) error {
|
|
tags, _ := cmd.Flags().GetString("tags")
|
|
client := api.NewClient()
|
|
return submitNote(client, args[0], tags)
|
|
}
|
|
|
|
func submitNote(client *api.Client, note, tags string) error {
|
|
fields := map[string]string{
|
|
"note": note,
|
|
}
|
|
if tags != "" {
|
|
fields["tags"] = tags
|
|
}
|
|
|
|
resp, err := client.PostMultipart("/api/v1/jobs", fields, nil)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if resp.StatusCode == http.StatusConflict {
|
|
var result interface{}
|
|
if err := api.DecodeJSON(resp, &result); err != nil {
|
|
return fmt.Errorf("failed to decode response: %w", err)
|
|
}
|
|
if output.IsJSON() {
|
|
output.PrintJSON(result)
|
|
} else {
|
|
if m, ok := result.(map[string]interface{}); ok {
|
|
if docID, ok := m["document_id"].(float64); ok {
|
|
fmt.Printf("Already imported: %s (doc ID: %.0f)\n", m["title"], docID)
|
|
} else if jobID, ok := m["job_id"].(float64); ok {
|
|
fmt.Printf("Already queued: %s (job ID: %.0f)\n", m["title"], jobID)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if err := api.CheckError(resp); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
var result interface{}
|
|
if err := api.DecodeJSON(resp, &result); err != nil {
|
|
return fmt.Errorf("failed to decode response: %w", err)
|
|
}
|
|
|
|
if output.IsJSON() {
|
|
output.PrintJSON(result)
|
|
} else {
|
|
fmt.Println("Queued: note")
|
|
}
|
|
return nil
|
|
}
|