feat: search cmd

chore: raw sql replaced with squirrel
fix: -n is now required for add cmd
This commit is contained in:
abelith committed 2026-08-15 18:38:26 +05:00
1 parent 80a90b65bb
commit 4447b7343f
9 files changed
+141 -26

No files matched your search

+17 -6
View File
@@ -1,22 +1,33 @@
# Changelog
## [Latest]
All notable changes to this project will be documented in this file.
## [0.2.1-alpha]- 2026-8-14
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.3.0-alpha] - 2026-08-15
### Added
- `search` command
- `-v` and `--version` flags for root command
### Changed
- Switched from raw sql to sql builder for easier dynamic query construction and easier maintenance
### Fixed
- Now `--name` parameter for `add` command is required
## [0.2.1-alpha] - 2026-08-14
### Fixed
- Prevented creating spells with reserved names
- Improved error messages
## [Unreleased]
## [0.2.0-alpha] - 2026-08-08
### Added
- Configuration options
## [Unreleased]
## [0.1.0-alpha] - 2026-08-04
### Added
+46 -14
View File
@@ -1,4 +1,4 @@
# spellbook 0.2.1-alpha
# spellbook 0.3.0-alpha
`spellbook` is a lightweight CLI command manager written in Go. It allows you to save complex shell commands as parameterized templates ("spells") and execute them instantly with custom arguments.
@@ -10,6 +10,7 @@
- [Configuration](#configuration)
- [Usage](#usage)
- [Upcoming Features](#upcoming-features)
- [Known issues](#known-issues)
---
@@ -34,7 +35,7 @@ git clone https://github.com/abelith/spellbook.git
cd spellbook
# Build static binary
CGO_ENABLED=0 go build -o spellbook .
CGO_ENABLED=0 go build -ldflags="-X main.Version=0.3.0-alpha" -o spellbook .
# Move to bin directory (for Linux/macOS)
mv spellbook $(go env GOPATH)/bin/
@@ -45,7 +46,7 @@ mv spellbook $(go env GOPATH)/bin/
## Configuration
### Env
- `SPELLBOOK_DB_PATH`: db location (example: `/spellbook/spellbook.db`)
- `SPELLBOOK_DB_PATH`: database file path (example: `/spellbook/spellbook.db`)
---
@@ -58,28 +59,56 @@ mv spellbook $(go env GOPATH)/bin/
Use `spellbook` to list prepared spells
### Searching through your spellbook
Use `spellbook search -n name -c cmd -d desc -t tags [query]` to find spells matching the query
| Name | Shorthand | Description |
|----------|-----------|------------------------------------------------------------------------------|
| `--name` | `-n` | Part of a spell name to search for |
| `--cmd` | `-c` | Part of a spell command template to search for |
| `--desc` | `-d` | Part of a spell description to search for |
| `--tags` | `-t` | Comma-separated list of tags to search for |
| `query` | | General pattern to search for through spell names, descriptions and commands |
> At least one of the parameters above is required
### Add new spell
Use `spellbook add -n name -t tags -d desc -- command` to add a new spell
- `-n` or `--name` is a name of a spell
- `-t` or `--tags` is a comma-separated list of spell tags
- `-d` or `--desc` is a short description of spell
- `command` is an actual spell template
Use `spellbook add -n name -t tags -d desc -- [command]` to add a new spell
| Name | Shorthand | Description | Required |
|-----------|-----------|------------------------------------|----------|
| `--name` | `-n` | Spell name | `Yes` |
| `--tags` | `-t` | Comma-separated list of spell tags | `No` |
| `--desc` | `-d` | Spell description | `No` |
| `command` | | Spell command template | `Yes` |
> It is advised to escape the `command` with single or double quotes
### Remove a spell
Use `spellbook rm [spell_name]` to remove a spell
- `spell_name` is a name of a spell
- `spell_name` is a name of a spell `required`
| Name | Description | Required |
|--------------|-------------|----------|
| `spell_name` | Spell name | `Yes` |
### Casting a spell
Use `spellbook [spell_name] key=val` to cast a spell
- `spell_name` is a name of a spell
- `key=val` are key/value arguments
Use `spellbook [spell_name] key=val key=val` to cast a spell
| Name | Description | Required |
|--------------|------------------------------|----------|
| `spell_name` | Spell name | `Yes` |
| `key=val` | key=val formatted parameters | `No` |
### Examples
Said, you have added a spell like this
For example, if you add a spell like this
```shell
spellbook add -n my-spell -t net,http -d my-desc -- curl 127.0.0.1
@@ -106,5 +135,8 @@ spellbook my-spell target=127.0.0.1
---
## Upcoming Features
- Search to navigate your spell book
- Export/import spellbooks
- Namespaces
## Known issues
- `KEY=VAL my-cmd` inline environment variable syntax is currently not supported (will fail with `executable file not found in $PATH`)
+4 -1
View File
@@ -16,6 +16,9 @@ import (
"syscall"
)
// Version is set on build via -ldflags
var Version = "dev"
func handleErr(err error) {
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
@@ -41,7 +44,7 @@ func main() {
app := application.NewApplication(&caster.SpellCaster{}, repo, repo)
c := cli.NewCLI(app)
c := cli.NewCLI(Version, app)
if err := c.Run(ctx); err != nil {
os.Exit(1)
+6
View File
@@ -13,6 +13,7 @@ var reservedCommands = map[string]struct{}{
"rm": {},
"help": {},
"completion": {},
"search": {},
}
func NewAddCommand(app Application) *cobra.Command {
@@ -34,6 +35,11 @@ func NewAddCommand(app Application) *cobra.Command {
}
spell.Spell = strings.Join(args, " ")
if spell.Name == "" {
return fmt.Errorf("%w: --name is missing", errors.ErrInsufficientArgs)
}
return app.AddSpell(ctx, spell)
},
}
+3 -2
View File
@@ -18,11 +18,12 @@ type CLI struct {
root *cobra.Command
}
func NewCLI(app Application) *CLI {
root := NewRootCmd(app)
func NewCLI(ver string, app Application) *CLI {
root := NewRootCmd(ver, app)
root.AddCommand(
NewAddCommand(app),
NewRmCommand(app),
NewSearchCmd(app),
)
return &CLI{
+4 -3
View File
@@ -81,15 +81,16 @@ func printTable(cmd *cobra.Command, spells []models.Spell) error {
return nil
}
func NewRootCmd(app Application) *cobra.Command {
func NewRootCmd(ver string, app Application) *cobra.Command {
cmd := &cobra.Command{
Use: "spellbook",
Short: "CLI tool to store, manage and execute command templates",
Long: `Spellbook is a lightweight binary command manager written in Go.
It allows you to save complex shell commands as parametrized templates (spells)
and execute them instantly with custom arguments from anywhere in your terminal`,
RunE: rootCmd(app),
Args: cobra.ArbitraryArgs,
RunE: rootCmd(app),
Args: cobra.ArbitraryArgs,
Version: ver,
}
return cmd
+47
View File
@@ -0,0 +1,47 @@
package cli
import (
"fmt"
"github.com/abelith/spellbook/internal/errors"
"github.com/abelith/spellbook/internal/models"
"github.com/spf13/cobra"
)
func NewSearchCmd(app Application) *cobra.Command {
query := models.SearchQuery{}
cmd := &cobra.Command{
Use: "search [query]",
Short: "Search for spells",
Long: "WIP",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if query.IsEmpty() {
return errors.ErrEmptyQuery
}
if len(args) > 0 {
query.Text = args[0]
}
spells, err := app.GetSpells(ctx, query)
if err != nil {
return err
}
if len(spells) < 1 {
return fmt.Errorf("%w: spells", errors.ErrNotFound)
}
return printTable(cmd, spells)
},
}
cmd.Flags().StringVarP(&query.Name, "name", "n", "", "Spell name")
cmd.Flags().StringVarP(&query.Command, "cmd", "c", "", "Spell command template")
cmd.Flags().StringVarP(&query.Description, "desc", "d", "", "Spell description")
cmd.Flags().StringSliceVarP(&query.Tags, "tags", "t", nil, "Spell tags")
return cmd
}
+2
View File
@@ -7,4 +7,6 @@ var (
ErrInsufficientArgs = fmt.Errorf("mandatory args are missing")
ErrFailedToCast = fmt.Errorf("failed to cast spell")
ErrNoDBProvided = fmt.Errorf("no db provided")
ErrNotFound = fmt.Errorf("not found")
ErrEmptyQuery = fmt.Errorf("empty search query")
)
+12
View File
@@ -0,0 +1,12 @@
package models
func (sq *SearchQuery) IsEmpty() bool {
isEmpty := sq.Text == "" &&
sq.Name == "" &&
sq.Command == "" &&
sq.Description == "" &&
len(sq.Tags) < 1 &&
sq.Cursor == 0 &&
sq.Limit == 0
return isEmpty
}