diff --git a/skills/scripting-with-go/SKILL.md b/skills/scripting-with-go/SKILL.md new file mode 100644 index 0000000000000000000000000000000000000000..9a0f78383886ed4058f7f8c5eb5918a8a743f049 --- /dev/null +++ b/skills/scripting-with-go/SKILL.md @@ -0,0 +1,135 @@ +--- +name: scripting-with-go +description: Creates executable Go scripts with shebang-like behavior. Use when the user wants Go scripts, mentions Go scripting, or needs executable .go files. If working in a Go project, do NOT use unless explicitly requested. +license: AGPL-3.0-or-later +metadata: + author: Amolith +--- + +Create executable Go scripts using a shell trick (not a true shebang). Scripts run directly like `./script.go` with full argument support. + +## Basic pattern + +First line of any Go script: + +```go +/**/usr/local/go/bin/go run "$0" "$@"; exit; +``` + +Then `chmod +x script.go` and run `./script.go args`. + +## How it works + +1. OS tries to execute `./script.go` as binary +2. Fails with ENOEXEC (not a binary) +3. OS falls back to `/bin/sh` +4. Shell runs first line: compiles and executes the script +5. `$0` = script path, `$@` = all arguments +6. `exit;` prevents shell from interpreting remaining Go code +7. `/**/` instead of `//` avoids gopls formatting complaints + +## Complete example + +```go +/**/usr/local/go/bin/go run "$0" "$@"; exit; +package main + +import ( + "fmt" + "os" +) + +func main() { + if len(os.Args) < 2 { + fmt.Println("Usage: ./script.go ") + os.Exit(1) + } + fmt.Printf("Hello, %s!\n", os.Args[1]) +} +``` + +## When to use + +**Good for:** +- Long-lived automation needing stability (Go 1.x compatibility guarantee) +- Cross-platform scripts +- Corporate environments +- Scripts that may grow into programs +- Avoiding dependency management hell + +**Avoid for:** +- Simple one-liners +- Systems without Go +- Throwaway scripts + +## Common patterns + +### CLI flags + +```go +/**/usr/local/go/bin/go run "$0" "$@"; exit; +package main + +import ( + "flag" + "fmt" +) + +func main() { + verbose := flag.Bool("v", false, "verbose output") + flag.Parse() + fmt.Printf("Verbose: %v, Args: %v\n", *verbose, flag.Args()) +} +``` + +### stdin + +```go +/**/usr/local/go/bin/go run "$0" "$@"; exit; +package main + +import ( + "bufio" + "fmt" + "os" +) + +func main() { + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + fmt.Println("Got:", scanner.Text()) + } +} +``` + +### File operations + +```go +/**/usr/local/go/bin/go run "$0" "$@"; exit; +package main + +import ( + "fmt" + "os" + "path/filepath" +) + +func main() { + filepath.Walk(".", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + fmt.Println(path) + } + return nil + }) +} +``` + +## Notes + +- Adjust `/usr/local/go/bin/go` if Go is elsewhere +- Semicolon required in `exit;` with `/**/` syntax +- Avoid dependencies for maximum compatibility +- Slower startup than interpreted languages (compilation time)