Reject single bare word as implicit note shorthand

Single unrecognized words now print an error with usage hint instead of
being submitted as a note. Prevents typos from creating junk notes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:03:52 +01:00
parent b2176c36ea
commit 2fa2ac1134
3 changed files with 64 additions and 3 deletions
+5 -2
View File
@@ -38,6 +38,9 @@ var rootCmd = &cobra.Command{
if len(args) == 0 {
return cmd.Help()
}
if len(args) == 1 {
return fmt.Errorf("unknown command %q\nTo add a note, use: kb \"%s ...\" or pass multiple words", args[0], args[0])
}
note := strings.Join(args, " ")
tags, _ := cmd.Flags().GetString("tags")
client := api.NewClient()
@@ -48,8 +51,8 @@ var rootCmd = &cobra.Command{
func init() {
api.SetVersionInfo(Version, MinEngineVersion)
rootCmd.Version = Version
rootCmd.SetUsageTemplate(`Quick note taking:
kb "note text" [flags]
rootCmd.SetUsageTemplate(`Quick note taking (must be more than one word):
kb "note text here" [flags]
Normal usage:
kb [command] [flags]{{if .HasAvailableSubCommands}}
+54
View File
@@ -0,0 +1,54 @@
package cmd
import (
"bytes"
"strings"
"testing"
)
func TestRootCmd_SingleWordRejected(t *testing.T) {
rootCmd.SetArgs([]string{"infow"})
var stderr bytes.Buffer
rootCmd.SetErr(&stderr)
err := rootCmd.Execute()
if err == nil {
t.Fatal("expected error for single bare word, got nil")
}
errMsg := err.Error()
if !strings.Contains(errMsg, `unknown command "infow"`) {
t.Errorf("expected error to mention unknown command, got: %s", errMsg)
}
if !strings.Contains(errMsg, "multiple words") {
t.Errorf("expected error to suggest multiple words, got: %s", errMsg)
}
}
func TestRootCmd_MultipleWordsNotRejected(t *testing.T) {
rootCmd.SetArgs([]string{"remember", "to", "update", "dns"})
err := rootCmd.Execute()
// Will fail at API call (no server), but should NOT be the "unknown command" error
if err != nil && strings.Contains(err.Error(), "unknown command") {
t.Errorf("multi-word input should not be rejected as unknown command, got: %s", err.Error())
}
}
func TestRootCmd_NoArgs_ShowsHelp(t *testing.T) {
rootCmd.SetArgs([]string{})
var stdout bytes.Buffer
rootCmd.SetOut(&stdout)
err := rootCmd.Execute()
if err != nil {
t.Fatalf("expected no error for zero args, got: %v", err)
}
output := stdout.String()
if !strings.Contains(output, "Available Commands") {
t.Errorf("expected help output, got: %s", output)
}
}
+5 -1
View File
@@ -70,7 +70,7 @@ The client SHALL provide a `kb search <query>` command that sends the query to t
### Requirement: Implicit note shorthand
The client SHALL treat bare string arguments (with no subcommand) as an implicit note. `kb "my note"` SHALL behave identically to submitting a note via `POST /api/v1/jobs`. All persistent flags (`--format`, `--engine`, `--api-key`) and the root `--tags` flag SHALL work with the shorthand form.
The client SHALL treat bare string arguments (with no subcommand) as an implicit note only when **more than one argument** is provided. `kb "my note"` SHALL behave identically to submitting a note via `POST /api/v1/jobs`. All persistent flags (`--format`, `--engine`, `--api-key`) and the root `--tags` flag SHALL work with the shorthand form. A single bare word SHALL be rejected with an error message.
#### Scenario: Quick note via bare argument
- **WHEN** the user runs `kb "remember to update DNS"`
@@ -92,6 +92,10 @@ The client SHALL treat bare string arguments (with no subcommand) as an implicit
- **WHEN** the user runs `kb remember to update dns` (without quotes)
- **THEN** the client SHALL join all arguments into a single note string and submit it
#### Scenario: Single bare word rejected
- **WHEN** the user runs `kb infow` (a single unrecognized word)
- **THEN** the client SHALL print to stderr: `Unknown command "infow". Run 'kb --help' for available commands.` followed by a hint about note usage, and exit with a non-zero code
#### Scenario: No interference with subcommands
- **WHEN** the user runs `kb search "query"` or any other existing subcommand
- **THEN** the client SHALL route to the subcommand as before — the implicit note shorthand SHALL NOT interfere