6 Commits

Author SHA1 Message Date
agresdominik f0056b9722 bump 2026-06-22 23:17:46 +02:00
agresdominik 39361150a5 fix to go to clipboard, release workflow 2026-06-22 23:13:12 +02:00
agresdominik 4b21757e16 chore: bump 2026-03-23 20:34:44 +01:00
agresdominik 45f66a07ad feature: add json validity test 2026-03-23 20:29:14 +01:00
agresdominik 74304b926d feature: update list, bump version to test 2026-03-23 20:23:22 +01:00
agresdominik 3c5ae1ef2f feature: add CD to tap repo 2026-03-23 20:20:04 +01:00
9 changed files with 164 additions and 77 deletions
+6 -1
View File
@@ -8,7 +8,7 @@ name: CI
on:
push:
branches: [main]
branches: ['**'] # CI on every branch
pull_request:
branches: [main]
@@ -24,6 +24,11 @@ jobs:
go-version-file: go.mod
cache: true
- name: Validate command JSON files
run: |
jq empty data/commands.json
jq empty data/commands_template.json
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
+20 -20
View File
@@ -83,25 +83,25 @@ jobs:
echo "Tarball URL : $URL"
echo "SHA256 : $SHA"
# - name: Checkout homebrew tap
# uses: actions/checkout@v4
# with:
# repository: TODO_GITHUB_USER/homebrew-myapps # TODO: your tap repo
# token: ${{ secrets.TAP_GITHUB_TOKEN }}
# path: tap
- name: Checkout homebrew tap
uses: actions/checkout@v4
with:
repository: agresdominik/repo
token: ${{ secrets.TAP_GITHUB_TOKEN }}
path: tap
#- name: Patch cheatsh formula
# run: |
# FORMULA="tap/Formula/cheatsh.rb"
# sed -i "s|url .*|url \"${{ steps.sha.outputs.url }}\"|" "$FORMULA"
# sed -i "s|version .*|version \"${{ inputs.version }}\"|" "$FORMULA"
# sed -i "s|sha256 .*|sha256 \"${{ steps.sha.outputs.sha }}\"|" "$FORMULA"
- name: Patch cheatsh formula
run: |
# version is inferred by Homebrew from the URL tag, so only url + sha256 are patched.
FORMULA="tap/Formula/cheatsh.rb"
sed -i "s|url .*|url \"${{ steps.sha.outputs.url }}\"|" "$FORMULA"
sed -i "s|sha256 .*|sha256 \"${{ steps.sha.outputs.sha }}\"|" "$FORMULA"
#- name: Commit and push tap update
# run: |
# cd tap
# git config user.name "github-actions[bot]"
# git config user.email "github-actions[bot]@users.noreply.github.com"
# git diff --quiet && echo "Formula already up to date." && exit 0
# git commit -am "formula: bump cheatsh to v${{ inputs.version }}"
# git push
- name: Commit and push tap update
run: |
cd tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git diff --quiet && echo "Formula already up to date." && exit 0
git commit -am "chore: bump cheatsh to v${{ inputs.version }}"
git push
+6 -6
View File
@@ -1,8 +1,8 @@
EXECUTABLE = cheatsh
PACKAGES = ./src/.
BINDIR = bin
SYSCONFDIR = $(HOME)/.config/cheatsh
PREFIX ?= $(HOME)/.local
DATADIR = $(PREFIX)/share/cheatsh
all: build
@@ -12,16 +12,16 @@ build:
install: build
@echo "Installing binary in $(PREFIX)"
@echo "Installing binary in $(PREFIX)/bin"
install -Dm755 bin/$(EXECUTABLE) $(PREFIX)/bin/$(EXECUTABLE)
@echo "Copying config files to $(SYSCONFDIR)"
install -Dm644 data/commands.json $(SYSCONFDIR)/commands.json
install -Dm644 data/commands_template.json $(SYSCONFDIR)/commands_template.json
@echo "Installing command lists in $(DATADIR)"
install -Dm644 data/commands.json $(DATADIR)/commands.json
install -Dm644 data/commands_template.json $(DATADIR)/commands_template.json
uninstall:
rm -f $(PREFIX)/bin/$(EXECUTABLE)
rm -rf $(SYSCONFDIR)
rm -rf $(DATADIR)
clean:
rm -rf bin/
+79 -5
View File
@@ -1,15 +1,89 @@
# Cheat Sheet
A small TUI Tool which displays a pre-configured json file content in a menu and lets the user search for commands and select them. Once selected these are printed in the terminal for easy access.
A small TUI tool that displays a pre-configured set of commands in a searchable
menu. Pick one and it's copied to your system clipboard — paste it where you
need it and edit before running.
I wrote this to simplify my search for commands I use often enough to need them multiple times but not often enoigh to remember alway exactly how they are called.
I wrote this to look up commands I use often enough to need repeatedly, but not
often enough to remember exactly. Written in Go with [bubbletea](https://github.com/charmbracelet/bubbletea).
Written in go + used bubbletea for the TUI part of the application.
## Install
### Homebrew (recommended)
```bash
brew install agresdominik/repo/cheatsh
```
### From source
```bash
git clone https://github.com/agresdominik/cheat_sheet
cd cheat_sheet
make install # builds and installs to ~/.local/bin
```
Make sure `~/.local/bin` is in your `PATH` (or change `PREFIX` in the `Makefile`).
## Configuration
cheatsh ships with a maintained command list and uses it by default. It lives
next to the binary and is **replaced on `brew upgrade`**, so updating the list
in this repo and releasing is all it takes to get the new commands on your
machines:
```
<prefix>/share/cheatsh/commands.json # Homebrew, e.g. /opt/homebrew/...
~/.local/share/cheatsh/commands.json # make install
data/commands.json # running from a source checkout
```
To maintain the default list, edit `data/commands.json` and release. The format
is a map of categories to command lists:
```json
{
"git": [
{ "command": "git reset --soft HEAD~1", "desc": "Undo last commit, keep changes" }
]
}
```
### Custom list
If you'd rather keep your own list (one that survives upgrades untouched), copy
the template and pass it explicitly — `--config` always wins over the bundled
default:
```bash
cp "$(brew --prefix)/share/cheatsh/commands_template.json" ~/my-commands.json
cheatsh --config ~/my-commands.json
```
## Clipboard dependency
Copying to the clipboard needs a backend:
- **macOS** — works out of the box (`pbcopy`).
- **Linux/Wayland** — `wl-clipboard`.
- **Linux/X11** — `xsel` or `xclip`.
If no backend is found, cheatsh prints the command to stdout instead.
## Usage
Use the commands in the given makefile to run, build, test in a docker container or install the application on your device. Beforehand define your own commands.json based on the `commands_template.json` in the `data/` folder.
```bash
cheatsh # browse and select; selection is copied to clipboard
cheatsh --config FILE # use a specific config file
cheatsh --help
```
Once installed just run `cheatsh` in the terminal. If it's not found, make sure `~/.local/bin/` is in your `PATH` or change the path under the `PREFIX` variable in the `Makefile`.
In the TUI: `/` to filter, `enter` to descend / select, `b` to go back, `q` to quit.
## Development
```bash
make build # build to bin/cheatsh
make local # build and run against data/commands.json
go test ./src/...
```
+1 -1
View File
@@ -1 +1 @@
0.1.0
0.1.3
+4
View File
@@ -223,6 +223,10 @@
{
"command": "pipx upgrade-all",
"desc": "Pip installed packages on system"
},
{
"command": "brew update",
"desc": "Brew installed own software"
}
],
"other installs": [
+1 -1
View File
@@ -3,13 +3,13 @@ module github.com/dominikagres/cheat_sheet
go 1.24.1
require (
github.com/atotto/clipboard v0.1.4
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
)
require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.10.1 // indirect
+36 -38
View File
@@ -40,56 +40,54 @@ func main() {
flag.Parse()
var configFile string
home, err := os.UserHomeDir()
if err == nil {
userConf := filepath.Join(home, ".config", "cheatsh")
if info, err := os.Stat(userConf); err == nil && info.IsDir() {
configFile = filepath.Join(userConf, "commands.json")
} else {
configFile = "/etc/cheatsh/commands.json"
}
}
var commands CmdList
if *configFlag != "" {
commands, err = loadCommands(*configFlag)
if err != nil {
log.Fatalf("Cannot load commands file: %v", err)
}
StartTui(commands)
return
} else if len(os.Args) == 1 {
commands, err = loadCommands(configFile)
if err != nil {
log.Fatalf("Cannot load commands file: %v", err)
}
StartTui(commands)
return
}
switch {
case *helpFlag:
printHelp()
return
case *newFlag:
HandleInput()
default:
printHelp()
os.Exit(1)
return
}
configFile := *configFlag
if configFile == "" {
configFile = defaultConfigPath()
}
commands, err := loadCommands(configFile)
if err != nil {
if os.IsNotExist(err) {
log.Fatalf("No config found at %s\nPass a config explicitly with --config <file>.", configFile)
}
log.Fatalf("Cannot load commands file: %v", err)
}
StartTui(commands)
}
// defaultConfigPath returns the bundled command list installed next to the
// binary (<prefix>/share/cheatsh/commands.json, replaced on upgrade). When
// running from a source checkout that file doesn't exist, so it falls back to
// data/commands.json relative to the working directory.
func defaultConfigPath() string {
if exe, err := os.Executable(); err == nil {
if real, err := filepath.EvalSymlinks(exe); err == nil {
exe = real
}
p := filepath.Join(filepath.Dir(exe), "..", "share", "cheatsh", "commands.json")
if _, err := os.Stat(p); err == nil {
return p
}
}
return filepath.Join("data", "commands.json")
}
func printHelp() {
fmt.Println(`Usage: cheatsh [options]
Select a command to copy it to the system clipboard.
Without --config the bundled command list is used; pass --config to use your own.
Options:
--config <file> Specify a config file
--config <file> Use a custom config file
--help Show this help message
--new Add a new command to the config file`)
}
+6
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"github.com/atotto/clipboard"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
@@ -128,7 +129,12 @@ func StartTui(commands CmdList) {
}
if m, ok := finalModel.(model); ok && m.selectedCmd != "" {
if err := clipboard.WriteAll(m.selectedCmd); err != nil {
fmt.Fprintln(os.Stderr, "Could not copy to clipboard:", err)
fmt.Println(m.selectedCmd)
return
}
fmt.Println("Copied to clipboard:", m.selectedCmd)
}
}