chore(commands): more commands for suggestions (#10)
## What's new - color completion message command green https://github.com/versenilvis/IRIS/pull/10/commits/6e141f5e6909fad1ec42725cce38d51729acf399 - new flat command folder structure https://github.com/versenilvis/IRIS/pull/10/commits/87026a854a529ec5d5baed967d5f7a7f0085a24b - fix some linter problems https://github.com/versenilvis/IRIS/pull/10/commits/536de790cefda912f50bf4022e40ce2b965db2e2 - fix some gemini fix suggestions
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/versenilvis/iris/commands/cc"
|
||||||
|
_ "github.com/versenilvis/iris/commands/fs"
|
||||||
|
_ "github.com/versenilvis/iris/commands/git"
|
||||||
|
_ "github.com/versenilvis/iris/commands/golang"
|
||||||
|
_ "github.com/versenilvis/iris/commands/js"
|
||||||
|
_ "github.com/versenilvis/iris/commands/jvm"
|
||||||
|
_ "github.com/versenilvis/iris/commands/ops"
|
||||||
|
_ "github.com/versenilvis/iris/commands/pkginstaller"
|
||||||
|
_ "github.com/versenilvis/iris/commands/python"
|
||||||
|
_ "github.com/versenilvis/iris/commands/runner"
|
||||||
|
_ "github.com/versenilvis/iris/commands/rust"
|
||||||
|
_ "github.com/versenilvis/iris/commands/sys"
|
||||||
|
_ "github.com/versenilvis/iris/commands/text"
|
||||||
|
_ "github.com/versenilvis/iris/commands/view"
|
||||||
|
)
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package cc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cOptions := []core.Option{
|
||||||
|
{Name: "-o", Description: "output file"},
|
||||||
|
{Name: "-c", Description: "compile only (no link)"},
|
||||||
|
{Name: "-g", Description: "debug symbols"},
|
||||||
|
{Name: "-O0", Description: "no optimization"},
|
||||||
|
{Name: "-O1", Description: "basic optimization"},
|
||||||
|
{Name: "-O2", Description: "standard optimization"},
|
||||||
|
{Name: "-O3", Description: "aggressive optimization"},
|
||||||
|
{Name: "-Os", Description: "optimize for size"},
|
||||||
|
{Name: "-Wall", Description: "enable common warnings"},
|
||||||
|
{Name: "-Wextra", Description: "extra warnings"},
|
||||||
|
{Name: "-Werror", Description: "treat warnings as errors"},
|
||||||
|
{Name: "-std", Description: "language standard (e.g. c11, c++17)"},
|
||||||
|
{Name: "-I", Description: "include directory"},
|
||||||
|
{Name: "-L", Description: "library directory"},
|
||||||
|
{Name: "-l", Description: "link library"},
|
||||||
|
{Name: "-D", Description: "define macro"},
|
||||||
|
{Name: "-shared", Description: "build shared library"},
|
||||||
|
{Name: "-static", Description: "static linking"},
|
||||||
|
{Name: "-fPIC", Description: "position independent code"},
|
||||||
|
{Name: "-march", Description: "target architecture"},
|
||||||
|
{Name: "-pthread", Description: "POSIX threads"},
|
||||||
|
{Name: "-v", Description: "verbose"},
|
||||||
|
{Name: "-E", Description: "preprocess only"},
|
||||||
|
{Name: "-S", Description: "compile to assembly"},
|
||||||
|
{Name: "-M", Description: "output dependencies"},
|
||||||
|
{Name: "-MMD", Description: "write dep file"},
|
||||||
|
{Name: "--version", Description: "print version"},
|
||||||
|
}
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "gcc",
|
||||||
|
Description: "GNU C compiler",
|
||||||
|
Generator: core.FileGenerator(".c", ".h"),
|
||||||
|
Options: cOptions,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "g++",
|
||||||
|
Description: "GNU C++ compiler",
|
||||||
|
Generator: core.FileGenerator(".cpp", ".cc", ".cxx", ".h", ".hpp"),
|
||||||
|
Options: cOptions,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "cc",
|
||||||
|
Description: "C compiler (alias)",
|
||||||
|
Generator: core.FileGenerator(".c", ".h"),
|
||||||
|
Options: cOptions,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "c++",
|
||||||
|
Description: "C++ compiler (alias)",
|
||||||
|
Generator: core.FileGenerator(".cpp", ".cc", ".cxx", ".h", ".hpp"),
|
||||||
|
Options: cOptions,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "clang",
|
||||||
|
Description: "LLVM C compiler",
|
||||||
|
Generator: core.FileGenerator(".c", ".h"),
|
||||||
|
Options: cOptions,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "clang++",
|
||||||
|
Description: "LLVM C++ compiler",
|
||||||
|
Generator: core.FileGenerator(".cpp", ".cc", ".cxx", ".h", ".hpp"),
|
||||||
|
Options: cOptions,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
package dev
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/versenilvis/iris/commands/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
core.Register(&core.Spec{
|
|
||||||
Name: "docker",
|
|
||||||
Description: "container engine",
|
|
||||||
Subcommands: []core.Subcommand{
|
|
||||||
{
|
|
||||||
Name: "ps",
|
|
||||||
Description: "list containers",
|
|
||||||
Options: []core.Option{
|
|
||||||
{Name: "-a", Description: "show all"},
|
|
||||||
{Name: "-q", Description: "only show IDs"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "build",
|
|
||||||
Description: "build image",
|
|
||||||
Options: []core.Option{
|
|
||||||
{Name: "-t", Description: "tag name"},
|
|
||||||
{Name: "-f", Description: "dockerfile path"},
|
|
||||||
{Name: "--no-cache", Description: "no build cache"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "run",
|
|
||||||
Description: "run container",
|
|
||||||
Options: []core.Option{
|
|
||||||
{Name: "-d", Description: "detached mode"},
|
|
||||||
{Name: "-p", Description: "port mapping"},
|
|
||||||
{Name: "-v", Description: "volume mount"},
|
|
||||||
{Name: "--rm", Description: "auto remove"},
|
|
||||||
{Name: "--name", Description: "container name"},
|
|
||||||
{Name: "-it", Description: "interactive tty"},
|
|
||||||
{Name: "-e", Description: "set env variable"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "pull",
|
|
||||||
Description: "pull image",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "push",
|
|
||||||
Description: "push image",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "exec",
|
|
||||||
Description: "exec in container",
|
|
||||||
Options: []core.Option{
|
|
||||||
{Name: "-it", Description: "interactive tty"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "stop",
|
|
||||||
Description: "stop container",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "rm",
|
|
||||||
Description: "remove container",
|
|
||||||
Options: []core.Option{
|
|
||||||
{Name: "-f", Description: "force remove"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "rmi",
|
|
||||||
Description: "remove image",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "images",
|
|
||||||
Description: "list images",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "logs",
|
|
||||||
Description: "view logs",
|
|
||||||
Options: []core.Option{
|
|
||||||
{Name: "-f", Description: "follow output"},
|
|
||||||
{Name: "--tail", Description: "last n lines"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "compose",
|
|
||||||
Description: "multi-container",
|
|
||||||
Subcommands: []core.Subcommand{
|
|
||||||
{Name: "up", Description: "start services"},
|
|
||||||
{Name: "down", Description: "stop services"},
|
|
||||||
{Name: "build", Description: "build services"},
|
|
||||||
{Name: "logs", Description: "view logs"},
|
|
||||||
{Name: "ps", Description: "list services"},
|
|
||||||
{Name: "exec", Description: "execute command"},
|
|
||||||
{Name: "restart", Description: "restart services"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "network",
|
|
||||||
Description: "manage networks",
|
|
||||||
Subcommands: []core.Subcommand{
|
|
||||||
{Name: "ls", Description: "list networks"},
|
|
||||||
{Name: "create", Description: "create network"},
|
|
||||||
{Name: "rm", Description: "remove network"},
|
|
||||||
{Name: "inspect", Description: "show details"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "volume",
|
|
||||||
Description: "manage volumes",
|
|
||||||
Subcommands: []core.Subcommand{
|
|
||||||
{Name: "ls", Description: "list volumes"},
|
|
||||||
{Name: "create", Description: "create volume"},
|
|
||||||
{Name: "rm", Description: "remove volume"},
|
|
||||||
{Name: "prune", Description: "remove unused"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package dev
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/versenilvis/iris/commands/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
core.Register(&core.Spec{
|
|
||||||
Name: "npm",
|
|
||||||
Description: "node packages",
|
|
||||||
Subcommands: []core.Subcommand{
|
|
||||||
{Name: "install", Description: "install packages"},
|
|
||||||
{Name: "run", Description: "run script",
|
|
||||||
Subcommands: []core.Subcommand{
|
|
||||||
{Name: "dev", Description: "development server"},
|
|
||||||
{Name: "build", Description: "build for production"},
|
|
||||||
{Name: "start", Description: "start application"},
|
|
||||||
{Name: "test", Description: "run test suite"},
|
|
||||||
{Name: "lint", Description: "run linter"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{Name: "test", Description: "run tests"},
|
|
||||||
{Name: "init", Description: "create package.json"},
|
|
||||||
{Name: "publish", Description: "publish package"},
|
|
||||||
{Name: "update", Description: "update packages"},
|
|
||||||
{Name: "uninstall", Description: "remove package"},
|
|
||||||
{Name: "ls", Description: "list installed"},
|
|
||||||
{Name: "audit", Description: "security audit"},
|
|
||||||
{Name: "cache", Description: "manage cache",
|
|
||||||
Subcommands: []core.Subcommand{
|
|
||||||
{Name: "clean", Description: "clear cache"},
|
|
||||||
{Name: "verify", Description: "verify cache"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package dev
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/versenilvis/iris/commands/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
core.Register(&core.Spec{
|
|
||||||
Name: "pip",
|
|
||||||
Description: "python packages",
|
|
||||||
Subcommands: []core.Subcommand{
|
|
||||||
{Name: "install", Description: "install package"},
|
|
||||||
{Name: "uninstall", Description: "remove package"},
|
|
||||||
{Name: "freeze", Description: "list packages"},
|
|
||||||
{Name: "list", Description: "list installed"},
|
|
||||||
{Name: "show", Description: "show package info"},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -13,8 +13,16 @@ func init() {
|
|||||||
{Name: "-a", Description: "all files"},
|
{Name: "-a", Description: "all files"},
|
||||||
{Name: "-l", Description: "long format"},
|
{Name: "-l", Description: "long format"},
|
||||||
{Name: "-h", Description: "human readable"},
|
{Name: "-h", Description: "human readable"},
|
||||||
|
{Name: "-la", Description: "long + all"},
|
||||||
{Name: "-R", Description: "recursive"},
|
{Name: "-R", Description: "recursive"},
|
||||||
{Name: "-t", Description: "sort by time"},
|
{Name: "-t", Description: "sort by time"},
|
||||||
|
{Name: "-S", Description: "sort by size"},
|
||||||
|
{Name: "-1", Description: "one per line"},
|
||||||
|
{Name: "--color", Description: "colorize output"},
|
||||||
|
{Name: "-d", Description: "list directories only"},
|
||||||
|
{Name: "-i", Description: "show inode numbers"},
|
||||||
|
{Name: "-s", Description: "show block size"},
|
||||||
|
{Name: "-r", Description: "reverse order"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package dev
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -17,6 +17,39 @@ func GitStashGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
|||||||
return getGitResults(tokens, "stash", "list", "--format=%gd: %gs")
|
return getGitResults(tokens, "stash", "list", "--format=%gd: %gs")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GitTagGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
return getGitResults(tokens, "tag", "-l")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GitCommitGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
cwd := core.GetCWD()
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, "git", "log", "--format=%h [%cr] %s", "-30")
|
||||||
|
cmd.Dir = cwd
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []core.Suggestion
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(line, " ", 2)
|
||||||
|
hash := parts[0]
|
||||||
|
desc := ""
|
||||||
|
if len(parts) == 2 {
|
||||||
|
desc = parts[1]
|
||||||
|
}
|
||||||
|
results = append(results, core.Suggestion{Cmd: hash, Desc: desc})
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
func getGitResults(tokens []string, args ...string) []core.Suggestion {
|
func getGitResults(tokens []string, args ...string) []core.Suggestion {
|
||||||
return getGitResultsFiltered(tokens, false, args...)
|
return getGitResultsFiltered(tokens, false, args...)
|
||||||
}
|
}
|
||||||
@@ -47,13 +80,18 @@ func getGitResultsFiltered(tokens []string, localOnly bool, args ...string) []co
|
|||||||
lines := strings.Split(string(out), "\n")
|
lines := strings.Split(string(out), "\n")
|
||||||
var results []core.Suggestion
|
var results []core.Suggestion
|
||||||
|
|
||||||
// more robust subcommand detection: find the first non-flag after "git"
|
// find the subcommand by skipping global flags that take arguments
|
||||||
subcommand := ""
|
subcommand := ""
|
||||||
for i := 1; i < len(tokens); i++ {
|
for i := 1; i < len(tokens); i++ {
|
||||||
if !strings.HasPrefix(tokens[i], "-") {
|
t := tokens[i]
|
||||||
subcommand = tokens[i]
|
if strings.HasPrefix(t, "-") {
|
||||||
break
|
if t == "-c" || t == "-C" || t == "--git-dir" || t == "--work-tree" || t == "--namespace" || t == "--super-prefix" || t == "--config-env" || t == "--exec-path" {
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subcommand = t
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
@@ -117,12 +155,8 @@ func getGitResultsFiltered(tokens []string, localOnly bool, args ...string) []co
|
|||||||
if activeBranch != "" {
|
if activeBranch != "" {
|
||||||
for i, r := range results {
|
for i, r := range results {
|
||||||
if r.Cmd == activeBranch {
|
if r.Cmd == activeBranch {
|
||||||
// move active branch to front if found
|
copy(results[1:i+1], results[0:i])
|
||||||
newResults := make([]core.Suggestion, 0, len(results))
|
results[0] = r
|
||||||
newResults = append(newResults, r)
|
|
||||||
newResults = append(newResults, results[:i]...)
|
|
||||||
newResults = append(newResults, results[i+1:]...)
|
|
||||||
results = newResults
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,9 +382,7 @@ func init() {
|
|||||||
{
|
{
|
||||||
Name: "tag",
|
Name: "tag",
|
||||||
Description: "manage tags",
|
Description: "manage tags",
|
||||||
Generator: func(tokens []string, prefix string, partial string) []core.Suggestion {
|
Generator: GitTagGenerator,
|
||||||
return getGitResults(tokens, "tag", "-l")
|
|
||||||
},
|
|
||||||
Options: []core.Option{
|
Options: []core.Option{
|
||||||
{Name: "-a", Description: "annotated tag"},
|
{Name: "-a", Description: "annotated tag"},
|
||||||
{Name: "-d", Description: "delete tag"},
|
{Name: "-d", Description: "delete tag"},
|
||||||
@@ -359,6 +391,29 @@ func init() {
|
|||||||
{Name: "-m", Description: "tag message"},
|
{Name: "-m", Description: "tag message"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "show",
|
||||||
|
Description: "show object",
|
||||||
|
Generator: func(tokens []string, prefix string, partial string) []core.Suggestion {
|
||||||
|
tags := GitTagGenerator(tokens, prefix, partial)
|
||||||
|
commits := GitCommitGenerator(tokens, prefix, partial)
|
||||||
|
return append(tags, commits...)
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "--stat", Description: "diffstat only"},
|
||||||
|
{Name: "--name-only", Description: "filenames only"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "revert",
|
||||||
|
Description: "revert a commit",
|
||||||
|
Generator: GitCommitGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "--no-commit", Description: "no auto commit"},
|
||||||
|
{Name: "--abort", Description: "abort revert"},
|
||||||
|
{Name: "--continue", Description: "continue revert"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "reset",
|
Name: "reset",
|
||||||
Description: "reset HEAD",
|
Description: "reset HEAD",
|
||||||
@@ -419,9 +474,32 @@ func init() {
|
|||||||
{
|
{
|
||||||
Name: "cherry-pick",
|
Name: "cherry-pick",
|
||||||
Description: "apply commit",
|
Description: "apply commit",
|
||||||
|
Generator: GitCommitGenerator,
|
||||||
Options: []core.Option{
|
Options: []core.Option{
|
||||||
{Name: "--no-commit", Description: "no auto commit"},
|
{Name: "--no-commit", Description: "no auto commit"},
|
||||||
{Name: "--abort", Description: "abort pick"},
|
{Name: "--abort", Description: "abort pick"},
|
||||||
|
{Name: "--continue", Description: "continue pick"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "worktree",
|
||||||
|
Description: "manage worktrees",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "add", Description: "add new worktree", Generator: core.FileGenerator("/")},
|
||||||
|
{Name: "list", Description: "list worktrees"},
|
||||||
|
{Name: "remove", Description: "remove worktree"},
|
||||||
|
{Name: "prune", Description: "prune stale worktrees"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "submodule",
|
||||||
|
Description: "manage submodules",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "add", Description: "add submodule"},
|
||||||
|
{Name: "init", Description: "init submodule config"},
|
||||||
|
{Name: "update", Description: "update submodules", Options: []core.Option{{Name: "--init", Description: "init if needed"}, {Name: "--recursive", Description: "recursive update"}}},
|
||||||
|
{Name: "status", Description: "show submodule status"},
|
||||||
|
{Name: "foreach", Description: "run command in each submodule"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package dev
|
package golang
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/versenilvis/iris/commands/core"
|
"github.com/versenilvis/iris/commands/core"
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package js
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "bun",
|
||||||
|
Description: "bun js runtime",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install packages", Options: []core.Option{
|
||||||
|
{Name: "--frozen-lockfile", Description: "no lockfile update"},
|
||||||
|
{Name: "--production", Description: "production only"},
|
||||||
|
{Name: "--no-save", Description: "don't update package.json"},
|
||||||
|
{Name: "--dry-run", Description: "print what would be installed"},
|
||||||
|
}},
|
||||||
|
{Name: "add", Description: "add package", Options: []core.Option{
|
||||||
|
{Name: "-d", Description: "dev dependency"},
|
||||||
|
{Name: "--dev", Description: "dev dependency (long form)"},
|
||||||
|
{Name: "--optional", Description: "optional dependency"},
|
||||||
|
{Name: "--peer", Description: "peer dependency"},
|
||||||
|
{Name: "-g", Description: "global"},
|
||||||
|
{Name: "--exact", Description: "exact version"},
|
||||||
|
{Name: "--no-save", Description: "don't update package.json"},
|
||||||
|
}},
|
||||||
|
{Name: "remove", Description: "remove package", Options: []core.Option{
|
||||||
|
{Name: "-g", Description: "remove global package"},
|
||||||
|
}},
|
||||||
|
{Name: "run", Description: "run script", Generator: NpmScriptGenerator, Options: []core.Option{
|
||||||
|
{Name: "--silent", Description: "suppress output"},
|
||||||
|
{Name: "--bun", Description: "force bun runtime"},
|
||||||
|
{Name: "--watch", Description: "watch mode"},
|
||||||
|
{Name: "--hot", Description: "hot reload"},
|
||||||
|
{Name: "--smol", Description: "reduce memory usage"},
|
||||||
|
}},
|
||||||
|
{Name: "build", Description: "bundle files", Generator: core.FileGenerator(".ts", ".tsx", ".js", ".jsx"), Options: []core.Option{
|
||||||
|
{Name: "--outdir", Description: "output directory"},
|
||||||
|
{Name: "--outfile", Description: "output file"},
|
||||||
|
{Name: "--minify", Description: "enable all minification"},
|
||||||
|
{Name: "--minify-syntax", Description: "minify syntax only"},
|
||||||
|
{Name: "--minify-whitespace", Description: "minify whitespace only"},
|
||||||
|
{Name: "--minify-identifiers", Description: "minify identifiers only"},
|
||||||
|
{Name: "--sourcemap", Description: "generate sourcemap (none/inline/external)"},
|
||||||
|
{Name: "--target", Description: "target environment (browser/bun/node)"},
|
||||||
|
{Name: "--format", Description: "output format (esm/cjs/iife)"},
|
||||||
|
{Name: "--splitting", Description: "enable code splitting"},
|
||||||
|
{Name: "--entry-naming", Description: "entry file naming pattern"},
|
||||||
|
{Name: "--chunk-naming", Description: "chunk file naming pattern"},
|
||||||
|
{Name: "--asset-naming", Description: "asset file naming pattern"},
|
||||||
|
{Name: "--watch", Description: "watch mode"},
|
||||||
|
{Name: "--external", Description: "mark package as external"},
|
||||||
|
{Name: "--define", Description: "replace global identifiers"},
|
||||||
|
{Name: "--loader", Description: "set file loader"},
|
||||||
|
{Name: "--public-path", Description: "prefix for public assets"},
|
||||||
|
}},
|
||||||
|
{Name: "test", Description: "run tests", Options: []core.Option{
|
||||||
|
{Name: "--watch", Description: "watch mode"},
|
||||||
|
{Name: "-t", Description: "filter by test name"},
|
||||||
|
{Name: "--coverage", Description: "collect coverage"},
|
||||||
|
{Name: "--bail", Description: "stop after first failure"},
|
||||||
|
{Name: "--timeout", Description: "test timeout in ms"},
|
||||||
|
{Name: "--rerun-each", Description: "rerun each test n times"},
|
||||||
|
{Name: "--only", Description: "run test.only tests"},
|
||||||
|
{Name: "--todo", Description: "show todo tests"},
|
||||||
|
{Name: "--reporter", Description: "test reporter (default/junit/tap)"},
|
||||||
|
}},
|
||||||
|
{Name: "x", Description: "execute package (bunx)", Options: []core.Option{
|
||||||
|
{Name: "--bun", Description: "force bun runtime"},
|
||||||
|
{Name: "--silent", Description: "suppress output"},
|
||||||
|
}},
|
||||||
|
{Name: "outdated", Description: "check outdated packages"},
|
||||||
|
{Name: "patch", Description: "patch a package", Options: []core.Option{
|
||||||
|
{Name: "--commit", Description: "commit the patch"},
|
||||||
|
}},
|
||||||
|
{Name: "publish", Description: "publish package to npm", Options: []core.Option{
|
||||||
|
{Name: "--dry-run", Description: "simulate publish"},
|
||||||
|
{Name: "--tag", Description: "dist-tag"},
|
||||||
|
{Name: "--access", Description: "public or restricted"},
|
||||||
|
}},
|
||||||
|
{Name: "init", Description: "create new project", Options: []core.Option{
|
||||||
|
{Name: "-y", Description: "skip prompts"},
|
||||||
|
}},
|
||||||
|
{Name: "create", Description: "create from template"},
|
||||||
|
{Name: "repl", Description: "launch bun repl"},
|
||||||
|
{Name: "upgrade", Description: "upgrade bun"},
|
||||||
|
{Name: "link", Description: "link package"},
|
||||||
|
{Name: "unlink", Description: "unlink package"},
|
||||||
|
{Name: "pm", Description: "package manager helpers", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "cache", Description: "show cache path"},
|
||||||
|
{Name: "ls", Description: "list packages"},
|
||||||
|
{Name: "hash", Description: "print lockfile hash"},
|
||||||
|
{Name: "hash-print", Description: "print resolved lockfile hash"},
|
||||||
|
{Name: "trust", Description: "trust package scripts"},
|
||||||
|
{Name: "untrusted", Description: "show untrusted packages"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "--watch", Description: "watch for changes"},
|
||||||
|
{Name: "--hot", Description: "hot module reload"},
|
||||||
|
{Name: "--smol", Description: "reduce memory usage"},
|
||||||
|
{Name: "--silent", Description: "suppress output"},
|
||||||
|
{Name: "--bun", Description: "force bun runtime"},
|
||||||
|
{Name: "--version", Description: "print version"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "bunx",
|
||||||
|
Description: "execute package (bun x)",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "--bun", Description: "force bun runtime"},
|
||||||
|
{Name: "--silent", Description: "suppress output"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package js
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NpmScriptGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
cwd := core.GetCWD()
|
||||||
|
data, err := os.ReadFile(filepath.Join(cwd, "package.json"))
|
||||||
|
if err != nil {
|
||||||
|
return []core.Suggestion{
|
||||||
|
{Cmd: "dev", Desc: "development server"},
|
||||||
|
{Cmd: "build", Desc: "build for production"},
|
||||||
|
{Cmd: "start", Desc: "start application"},
|
||||||
|
{Cmd: "test", Desc: "run test suite"},
|
||||||
|
{Cmd: "lint", Desc: "run linter"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pkg struct {
|
||||||
|
Scripts map[string]string `json:"scripts"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &pkg); err != nil || len(pkg.Scripts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
priority := []string{"dev", "start", "build", "test", "lint", "preview", "typecheck", "format"}
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var results []core.Suggestion
|
||||||
|
|
||||||
|
for _, name := range priority {
|
||||||
|
if cmd, ok := pkg.Scripts[name]; ok {
|
||||||
|
results = append(results, core.Suggestion{Cmd: name, Desc: cmd})
|
||||||
|
seen[name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, cmd := range pkg.Scripts {
|
||||||
|
if !seen[name] {
|
||||||
|
results = append(results, core.Suggestion{Cmd: name, Desc: cmd})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "npm",
|
||||||
|
Description: "node packages",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install packages", Options: []core.Option{
|
||||||
|
{Name: "--save-dev", Description: "save as devDependency"},
|
||||||
|
{Name: "--save-exact", Description: "exact version"},
|
||||||
|
{Name: "--legacy-peer-deps", Description: "skip peer deps"},
|
||||||
|
}},
|
||||||
|
{Name: "run", Description: "run script", Generator: NpmScriptGenerator},
|
||||||
|
{Name: "test", Description: "run tests"},
|
||||||
|
{Name: "init", Description: "create package.json", Options: []core.Option{
|
||||||
|
{Name: "-y", Description: "skip prompts"},
|
||||||
|
}},
|
||||||
|
{Name: "publish", Description: "publish package"},
|
||||||
|
{Name: "update", Description: "update packages"},
|
||||||
|
{Name: "uninstall", Description: "remove package"},
|
||||||
|
{Name: "ls", Description: "list installed"},
|
||||||
|
{Name: "audit", Description: "security audit", Options: []core.Option{
|
||||||
|
{Name: "fix", Description: "auto fix"},
|
||||||
|
}},
|
||||||
|
{Name: "ci", Description: "clean install from lockfile"},
|
||||||
|
{Name: "pack", Description: "create tarball"},
|
||||||
|
{Name: "link", Description: "symlink package"},
|
||||||
|
{Name: "cache", Description: "manage cache", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "clean", Description: "clear cache"},
|
||||||
|
{Name: "verify", Description: "verify cache"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "--prefix", Description: "set working directory"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package js
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "pnpm",
|
||||||
|
Description: "fast node packages",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install packages", Options: []core.Option{
|
||||||
|
{Name: "--frozen-lockfile", Description: "no lockfile update"},
|
||||||
|
{Name: "--prod", Description: "production only"},
|
||||||
|
}},
|
||||||
|
{Name: "add", Description: "add package", Options: []core.Option{
|
||||||
|
{Name: "-D", Description: "save as devDependency"},
|
||||||
|
{Name: "-g", Description: "install globally"},
|
||||||
|
{Name: "-E", Description: "exact version"},
|
||||||
|
}},
|
||||||
|
{Name: "remove", Description: "remove package"},
|
||||||
|
{Name: "run", Description: "run script", Generator: NpmScriptGenerator},
|
||||||
|
{Name: "build", Description: "build project"},
|
||||||
|
{Name: "dev", Description: "start dev server"},
|
||||||
|
{Name: "test", Description: "run tests"},
|
||||||
|
{Name: "update", Description: "update packages", Options: []core.Option{
|
||||||
|
{Name: "--latest", Description: "update to latest"},
|
||||||
|
{Name: "-i", Description: "interactive"},
|
||||||
|
}},
|
||||||
|
{Name: "store", Description: "manage store", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "prune", Description: "remove unreferenced"},
|
||||||
|
{Name: "status", Description: "check store"},
|
||||||
|
{Name: "path", Description: "print store path"},
|
||||||
|
}},
|
||||||
|
{Name: "exec", Description: "exec shell command"},
|
||||||
|
{Name: "dlx", Description: "fetch and run package"},
|
||||||
|
{Name: "ls", Description: "list packages"},
|
||||||
|
{Name: "audit", Description: "security audit"},
|
||||||
|
{Name: "patch", Description: "patch package"},
|
||||||
|
{Name: "init", Description: "create package.json"},
|
||||||
|
{Name: "publish", Description: "publish package"},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "--filter", Description: "filter by package"},
|
||||||
|
{Name: "-r", Description: "recursive"},
|
||||||
|
{Name: "--workspace-root", Description: "run in workspace root"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package js
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "yarn",
|
||||||
|
Description: "yarn package manager",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install packages", Options: []core.Option{
|
||||||
|
{Name: "--frozen-lockfile", Description: "error on lockfile change"},
|
||||||
|
{Name: "--production", Description: "production only"},
|
||||||
|
}},
|
||||||
|
{Name: "add", Description: "add package", Options: []core.Option{
|
||||||
|
{Name: "-D", Description: "dev dependency"},
|
||||||
|
{Name: "-P", Description: "peer dependency"},
|
||||||
|
{Name: "-E", Description: "exact version"},
|
||||||
|
{Name: "-g", Description: "global"},
|
||||||
|
}},
|
||||||
|
{Name: "remove", Description: "remove package"},
|
||||||
|
{Name: "run", Description: "run script", Generator: NpmScriptGenerator},
|
||||||
|
{Name: "build", Description: "build project"},
|
||||||
|
{Name: "dev", Description: "start dev server"},
|
||||||
|
{Name: "test", Description: "run tests"},
|
||||||
|
{Name: "upgrade", Description: "upgrade packages", Options: []core.Option{
|
||||||
|
{Name: "--latest", Description: "upgrade to latest"},
|
||||||
|
{Name: "-i", Description: "interactive"},
|
||||||
|
}},
|
||||||
|
{Name: "workspace", Description: "run command in workspace"},
|
||||||
|
{Name: "workspaces", Description: "list workspaces", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "info", Description: "workspace info"},
|
||||||
|
{Name: "run", Description: "run in all workspaces"},
|
||||||
|
}},
|
||||||
|
{Name: "link", Description: "symlink package"},
|
||||||
|
{Name: "unlink", Description: "unlink package"},
|
||||||
|
{Name: "audit", Description: "security audit"},
|
||||||
|
{Name: "cache", Description: "manage cache", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "clean", Description: "clear cache"},
|
||||||
|
{Name: "list", Description: "list cache"},
|
||||||
|
{Name: "dir", Description: "print cache dir"},
|
||||||
|
}},
|
||||||
|
{Name: "init", Description: "create package.json", Options: []core.Option{
|
||||||
|
{Name: "-2", Description: "berry (v2+)"},
|
||||||
|
}},
|
||||||
|
{Name: "publish", Description: "publish package"},
|
||||||
|
{Name: "info", Description: "show package info"},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "--cwd", Description: "working directory"},
|
||||||
|
{Name: "--verbose", Description: "verbose output"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package jvm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "javac",
|
||||||
|
Description: "Java compiler",
|
||||||
|
Generator: core.FileGenerator(".java"),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-d", Description: "output directory"},
|
||||||
|
{Name: "-cp", Description: "class path"},
|
||||||
|
{Name: "-classpath", Description: "class path (long form)"},
|
||||||
|
{Name: "-sourcepath", Description: "source path"},
|
||||||
|
{Name: "-g", Description: "debug info"},
|
||||||
|
{Name: "-verbose", Description: "verbose"},
|
||||||
|
{Name: "-encoding", Description: "source file encoding"},
|
||||||
|
{Name: "--release", Description: "target release version"},
|
||||||
|
{Name: "-source", Description: "source version"},
|
||||||
|
{Name: "-target", Description: "target version"},
|
||||||
|
{Name: "-Xlint", Description: "enable warnings"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "java",
|
||||||
|
Description: "Java runtime",
|
||||||
|
Generator: core.FileGenerator(".jar", ".class"),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-jar", Description: "run jar file"},
|
||||||
|
{Name: "-cp", Description: "class path"},
|
||||||
|
{Name: "-Xmx", Description: "max heap size (e.g. -Xmx512m)"},
|
||||||
|
{Name: "-Xms", Description: "initial heap size"},
|
||||||
|
{Name: "-D", Description: "set system property"},
|
||||||
|
{Name: "-ea", Description: "enable assertions"},
|
||||||
|
{Name: "--version", Description: "print version"},
|
||||||
|
{Name: "-server", Description: "server VM"},
|
||||||
|
{Name: "-verbose", Description: "verbose class loading"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "kotlinc",
|
||||||
|
Description: "Kotlin compiler",
|
||||||
|
Generator: core.FileGenerator(".kt", ".kts"),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-d", Description: "output dir or jar"},
|
||||||
|
{Name: "-cp", Description: "class path"},
|
||||||
|
{Name: "-include-runtime", Description: "include runtime in jar"},
|
||||||
|
{Name: "-jvm-target", Description: "JVM target version"},
|
||||||
|
{Name: "-script", Description: "run as script"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
package ops
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dockerContainerGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
cwd := core.GetCWD()
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, "docker", "ps", "-a", "--format={{.Names}}")
|
||||||
|
cmd.Dir = cwd
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []core.Suggestion
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results = append(results, core.Suggestion{Cmd: line, Desc: "container"})
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func dockerRunningContainerGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
cwd := core.GetCWD()
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, "docker", "ps", "--format={{.Names}}")
|
||||||
|
cmd.Dir = cwd
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []core.Suggestion
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results = append(results, core.Suggestion{Cmd: line, Desc: "running"})
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func dockerImageGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
cwd := core.GetCWD()
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, "docker", "images", "--format={{.Repository}}:{{.Tag}}")
|
||||||
|
cmd.Dir = cwd
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var results []core.Suggestion
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" || line == "<none>:<none>" || seen[line] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[line] = true
|
||||||
|
results = append(results, core.Suggestion{Cmd: line, Desc: "image"})
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "docker",
|
||||||
|
Description: "container engine",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{
|
||||||
|
Name: "ps",
|
||||||
|
Description: "list containers",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-a", Description: "show all"},
|
||||||
|
{Name: "-q", Description: "only show IDs"},
|
||||||
|
{Name: "--format", Description: "format output"},
|
||||||
|
{Name: "--filter", Description: "filter by condition"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "build",
|
||||||
|
Description: "build image",
|
||||||
|
Generator: core.FileGenerator(".dockerfile", ".Dockerfile"),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-t", Description: "tag name"},
|
||||||
|
{Name: "-f", Description: "dockerfile path"},
|
||||||
|
{Name: "--no-cache", Description: "no build cache"},
|
||||||
|
{Name: "--platform", Description: "target platform"},
|
||||||
|
{Name: "--build-arg", Description: "build argument"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "run",
|
||||||
|
Description: "run container",
|
||||||
|
Generator: dockerImageGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-d", Description: "detached mode"},
|
||||||
|
{Name: "-p", Description: "port mapping"},
|
||||||
|
{Name: "-v", Description: "volume mount"},
|
||||||
|
{Name: "--rm", Description: "auto remove"},
|
||||||
|
{Name: "--name", Description: "container name"},
|
||||||
|
{Name: "-it", Description: "interactive tty"},
|
||||||
|
{Name: "-e", Description: "set env variable"},
|
||||||
|
{Name: "--network", Description: "network mode"},
|
||||||
|
{Name: "--restart", Description: "restart policy"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "pull",
|
||||||
|
Description: "pull image",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "push",
|
||||||
|
Description: "push image",
|
||||||
|
Generator: dockerImageGenerator,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "exec",
|
||||||
|
Description: "exec in container",
|
||||||
|
Generator: dockerRunningContainerGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-it", Description: "interactive tty"},
|
||||||
|
{Name: "-e", Description: "set env variable"},
|
||||||
|
{Name: "-u", Description: "run as user"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "stop",
|
||||||
|
Description: "stop container",
|
||||||
|
Generator: dockerRunningContainerGenerator,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "start",
|
||||||
|
Description: "start container",
|
||||||
|
Generator: dockerContainerGenerator,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "restart",
|
||||||
|
Description: "restart container",
|
||||||
|
Generator: dockerContainerGenerator,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rm",
|
||||||
|
Description: "remove container",
|
||||||
|
Generator: dockerContainerGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-f", Description: "force remove"},
|
||||||
|
{Name: "-v", Description: "remove volumes"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rmi",
|
||||||
|
Description: "remove image",
|
||||||
|
Generator: dockerImageGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-f", Description: "force remove"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "images",
|
||||||
|
Description: "list images",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-a", Description: "show all"},
|
||||||
|
{Name: "-q", Description: "only IDs"},
|
||||||
|
{Name: "--format", Description: "format output"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "logs",
|
||||||
|
Description: "view logs",
|
||||||
|
Generator: dockerContainerGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-f", Description: "follow output"},
|
||||||
|
{Name: "--tail", Description: "last n lines"},
|
||||||
|
{Name: "--since", Description: "since timestamp"},
|
||||||
|
{Name: "-t", Description: "show timestamps"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "inspect",
|
||||||
|
Description: "show low-level info",
|
||||||
|
Generator: func(tokens []string, prefix string, partial string) []core.Suggestion {
|
||||||
|
containers := dockerContainerGenerator(tokens, prefix, partial)
|
||||||
|
images := dockerImageGenerator(tokens, prefix, partial)
|
||||||
|
return append(containers, images...)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "compose",
|
||||||
|
Description: "multi-container",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "up", Description: "start services", Options: []core.Option{{Name: "-d", Description: "detached"}, {Name: "--build", Description: "rebuild images"}}},
|
||||||
|
{Name: "down", Description: "stop services", Options: []core.Option{{Name: "-v", Description: "remove volumes"}}},
|
||||||
|
{Name: "build", Description: "build services"},
|
||||||
|
{Name: "logs", Description: "view logs", Options: []core.Option{{Name: "-f", Description: "follow"}}},
|
||||||
|
{Name: "ps", Description: "list services"},
|
||||||
|
{Name: "exec", Description: "execute command"},
|
||||||
|
{Name: "restart", Description: "restart services"},
|
||||||
|
{Name: "pull", Description: "pull images"},
|
||||||
|
{Name: "stop", Description: "stop services"},
|
||||||
|
{Name: "start", Description: "start services"},
|
||||||
|
{Name: "config", Description: "validate config", Generator: core.FileGenerator(".yml", ".yaml")},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-f", Description: "compose file"},
|
||||||
|
{Name: "--project-name", Description: "project name"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "network",
|
||||||
|
Description: "manage networks",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "ls", Description: "list networks"},
|
||||||
|
{Name: "create", Description: "create network"},
|
||||||
|
{Name: "rm", Description: "remove network"},
|
||||||
|
{Name: "inspect", Description: "show details"},
|
||||||
|
{Name: "prune", Description: "remove unused"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "volume",
|
||||||
|
Description: "manage volumes",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "ls", Description: "list volumes"},
|
||||||
|
{Name: "create", Description: "create volume"},
|
||||||
|
{Name: "rm", Description: "remove volume"},
|
||||||
|
{Name: "prune", Description: "remove unused"},
|
||||||
|
{Name: "inspect", Description: "show details"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "system",
|
||||||
|
Description: "manage docker system",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "prune", Description: "remove unused data", Options: []core.Option{{Name: "-a", Description: "remove all unused"}, {Name: "--volumes", Description: "include volumes"}}},
|
||||||
|
{Name: "df", Description: "show disk usage"},
|
||||||
|
{Name: "info", Description: "system info"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "docker-compose",
|
||||||
|
Description: "multi-container (legacy)",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "up", Description: "start services", Options: []core.Option{{Name: "-d", Description: "detached"}, {Name: "--build", Description: "rebuild"}}},
|
||||||
|
{Name: "down", Description: "stop services", Options: []core.Option{{Name: "-v", Description: "remove volumes"}}},
|
||||||
|
{Name: "build", Description: "build services"},
|
||||||
|
{Name: "logs", Description: "view logs", Options: []core.Option{{Name: "-f", Description: "follow"}}},
|
||||||
|
{Name: "ps", Description: "list services"},
|
||||||
|
{Name: "exec", Description: "execute command"},
|
||||||
|
{Name: "restart", Description: "restart services"},
|
||||||
|
{Name: "pull", Description: "pull images"},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-f", Description: "compose file"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package dev
|
package ops
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/versenilvis/iris/commands/core"
|
"github.com/versenilvis/iris/commands/core"
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package ops
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sshHostGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
paths := []string{
|
||||||
|
filepath.Join(home, ".ssh", "config"),
|
||||||
|
"/etc/ssh/ssh_config",
|
||||||
|
}
|
||||||
|
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var results []core.Suggestion
|
||||||
|
|
||||||
|
for _, path := range paths {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
parts := strings.Fields(scanner.Text())
|
||||||
|
if len(parts) < 2 || !strings.EqualFold(parts[0], "host") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range parts[1:] {
|
||||||
|
// skip wildcards
|
||||||
|
if strings.ContainsAny(host, "*?!") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if seen[host] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[host] = true
|
||||||
|
results = append(results, core.Suggestion{Cmd: host, Desc: "ssh host"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = scanner.Err()
|
||||||
|
_ = f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
sshOptions := []core.Option{
|
||||||
|
{Name: "-p", Description: "port"},
|
||||||
|
{Name: "-i", Description: "identity file"},
|
||||||
|
{Name: "-L", Description: "local port forward"},
|
||||||
|
{Name: "-R", Description: "remote port forward"},
|
||||||
|
{Name: "-D", Description: "dynamic socks proxy"},
|
||||||
|
{Name: "-N", Description: "no remote command"},
|
||||||
|
{Name: "-f", Description: "background"},
|
||||||
|
{Name: "-v", Description: "verbose"},
|
||||||
|
{Name: "-A", Description: "agent forwarding"},
|
||||||
|
{Name: "-X", Description: "x11 forwarding"},
|
||||||
|
}
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "ssh",
|
||||||
|
Description: "secure shell",
|
||||||
|
Generator: sshHostGenerator,
|
||||||
|
Options: sshOptions,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "scp",
|
||||||
|
Description: "secure copy",
|
||||||
|
Generator: sshHostGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-r", Description: "recursive"},
|
||||||
|
{Name: "-p", Description: "port"},
|
||||||
|
{Name: "-i", Description: "identity file"},
|
||||||
|
{Name: "-P", Description: "remote port"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "rsync",
|
||||||
|
Description: "remote sync",
|
||||||
|
Generator: sshHostGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-av", Description: "archive + verbose"},
|
||||||
|
{Name: "-z", Description: "compress"},
|
||||||
|
{Name: "--delete", Description: "delete extraneous"},
|
||||||
|
{Name: "--progress", Description: "show progress"},
|
||||||
|
{Name: "-e", Description: "remote shell command"},
|
||||||
|
{Name: "--exclude", Description: "exclude pattern"},
|
||||||
|
{Name: "--dry-run", Description: "simulation only"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
package pkginstaller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func installedPackageGenerator(pm string) core.GeneratorFunc {
|
||||||
|
return func(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
switch pm {
|
||||||
|
case "pacman", "yay", "paru":
|
||||||
|
cmd = exec.CommandContext(ctx, pm, "-Qq")
|
||||||
|
case "apt", "apt-get":
|
||||||
|
cmd = exec.CommandContext(ctx, "dpkg-query", "-W", "-f=${Package}\n")
|
||||||
|
case "dnf", "yum":
|
||||||
|
cmd = exec.CommandContext(ctx, "rpm", "-qa", "--qf", "%{NAME}\n")
|
||||||
|
case "brew":
|
||||||
|
cmd = exec.CommandContext(ctx, "brew", "list")
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []core.Suggestion
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
results = append(results, core.Suggestion{Cmd: line, Desc: "installed"})
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// pacman
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "pacman",
|
||||||
|
Description: "Arch package manager",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-S", Description: "install package"},
|
||||||
|
{Name: "-Syu", Description: "full system upgrade"},
|
||||||
|
{Name: "-Sy", Description: "sync database"},
|
||||||
|
{Name: "-Su", Description: "upgrade installed"},
|
||||||
|
{Name: "-Ss", Description: "search in repos"},
|
||||||
|
{Name: "-Si", Description: "show package info"},
|
||||||
|
{Name: "-R", Description: "remove package"},
|
||||||
|
{Name: "-Rs", Description: "remove + unused deps"},
|
||||||
|
{Name: "-Rns", Description: "remove + deps + configs"},
|
||||||
|
{Name: "-Q", Description: "query installed"},
|
||||||
|
{Name: "-Qq", Description: "query names only"},
|
||||||
|
{Name: "-Qs", Description: "search installed"},
|
||||||
|
{Name: "-Qi", Description: "installed package info"},
|
||||||
|
{Name: "-Qo", Description: "which package owns file"},
|
||||||
|
{Name: "-Ql", Description: "list package files"},
|
||||||
|
{Name: "-U", Description: "install local package"},
|
||||||
|
{Name: "-D", Description: "set install reason"},
|
||||||
|
{Name: "--noconfirm", Description: "skip confirmations"},
|
||||||
|
{Name: "--needed", Description: "skip up-to-date"},
|
||||||
|
{Name: "--asdeps", Description: "mark as dependency"},
|
||||||
|
{Name: "--asexplicit", Description: "mark as explicit"},
|
||||||
|
},
|
||||||
|
Generator: installedPackageGenerator("pacman"),
|
||||||
|
})
|
||||||
|
|
||||||
|
// yay
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "yay",
|
||||||
|
Description: "AUR helper (pacman wrapper)",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-S", Description: "install package (AUR + repos)"},
|
||||||
|
{Name: "-Syu", Description: "full system upgrade + AUR"},
|
||||||
|
{Name: "-Ss", Description: "search AUR + repos"},
|
||||||
|
{Name: "-Si", Description: "show package info"},
|
||||||
|
{Name: "-R", Description: "remove package"},
|
||||||
|
{Name: "-Rs", Description: "remove + unused deps"},
|
||||||
|
{Name: "-Rns", Description: "remove + deps + configs"},
|
||||||
|
{Name: "-Q", Description: "query installed"},
|
||||||
|
{Name: "-Qs", Description: "search installed"},
|
||||||
|
{Name: "-Qi", Description: "installed package info"},
|
||||||
|
{Name: "-Yc", Description: "remove unneeded deps"},
|
||||||
|
{Name: "-Y", Description: "yay-specific options"},
|
||||||
|
{Name: "--aur", Description: "only AUR results"},
|
||||||
|
{Name: "--repo", Description: "only repo results"},
|
||||||
|
{Name: "--devel", Description: "update devel packages"},
|
||||||
|
{Name: "--noconfirm", Description: "skip confirmations"},
|
||||||
|
{Name: "--needed", Description: "skip up-to-date"},
|
||||||
|
},
|
||||||
|
Generator: installedPackageGenerator("yay"),
|
||||||
|
})
|
||||||
|
|
||||||
|
// paru
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "paru",
|
||||||
|
Description: "AUR helper (feature-rich)",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-S", Description: "install package"},
|
||||||
|
{Name: "-Syu", Description: "full upgrade"},
|
||||||
|
{Name: "-Ss", Description: "search"},
|
||||||
|
{Name: "-R", Description: "remove"},
|
||||||
|
{Name: "-Rs", Description: "remove + deps"},
|
||||||
|
{Name: "-Q", Description: "query installed"},
|
||||||
|
{Name: "--fm", Description: "file manager for PKGBUILDs"},
|
||||||
|
{Name: "--noconfirm", Description: "skip confirmations"},
|
||||||
|
{Name: "--aur", Description: "AUR only"},
|
||||||
|
{Name: "--repo", Description: "repo only"},
|
||||||
|
},
|
||||||
|
Generator: installedPackageGenerator("paru"),
|
||||||
|
})
|
||||||
|
|
||||||
|
// apt
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "apt",
|
||||||
|
Description: "Debian/Ubuntu package manager",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install packages"},
|
||||||
|
{Name: "remove", Description: "remove packages", Generator: installedPackageGenerator("apt")},
|
||||||
|
{Name: "purge", Description: "remove + config files", Generator: installedPackageGenerator("apt")},
|
||||||
|
{Name: "autoremove", Description: "remove unused deps"},
|
||||||
|
{Name: "update", Description: "refresh package lists"},
|
||||||
|
{Name: "upgrade", Description: "upgrade installed"},
|
||||||
|
{Name: "full-upgrade", Description: "upgrade with auto remove"},
|
||||||
|
{Name: "dist-upgrade", Description: "smart upgrade"},
|
||||||
|
{Name: "search", Description: "search packages"},
|
||||||
|
{Name: "show", Description: "show package info"},
|
||||||
|
{Name: "list", Description: "list packages", Options: []core.Option{
|
||||||
|
{Name: "--installed", Description: "installed only"},
|
||||||
|
{Name: "--upgradable", Description: "upgradable only"},
|
||||||
|
}},
|
||||||
|
{Name: "download", Description: "download package (no install)"},
|
||||||
|
{Name: "clean", Description: "clear local repo"},
|
||||||
|
{Name: "autoclean", Description: "clear old cached packages"},
|
||||||
|
{Name: "depends", Description: "show dependencies"},
|
||||||
|
{Name: "rdepends", Description: "show reverse dependencies"},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-y", Description: "yes to all"},
|
||||||
|
{Name: "--no-install-recommends", Description: "skip recommends"},
|
||||||
|
{Name: "--dry-run", Description: "simulate"},
|
||||||
|
{Name: "-q", Description: "quiet"},
|
||||||
|
{Name: "--fix-broken", Description: "fix broken deps"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// apt-get
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "apt-get",
|
||||||
|
Description: "Debian/Ubuntu package manager (low-level)",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install packages"},
|
||||||
|
{Name: "remove", Description: "remove packages", Generator: installedPackageGenerator("apt")},
|
||||||
|
{Name: "purge", Description: "remove + configs", Generator: installedPackageGenerator("apt")},
|
||||||
|
{Name: "autoremove", Description: "remove unused deps"},
|
||||||
|
{Name: "update", Description: "refresh package lists"},
|
||||||
|
{Name: "upgrade", Description: "upgrade installed"},
|
||||||
|
{Name: "dist-upgrade", Description: "smart upgrade"},
|
||||||
|
{Name: "clean", Description: "clean cache"},
|
||||||
|
{Name: "autoclean", Description: "clean old packages"},
|
||||||
|
{Name: "download", Description: "download only"},
|
||||||
|
{Name: "source", Description: "get source package"},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-y", Description: "yes to all"},
|
||||||
|
{Name: "--no-install-recommends", Description: "skip recommends"},
|
||||||
|
{Name: "-q", Description: "quiet"},
|
||||||
|
{Name: "-s", Description: "simulate"},
|
||||||
|
{Name: "--fix-missing", Description: "fix missing"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// dnf (Fedora/RHEL)
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "dnf",
|
||||||
|
Description: "Fedora/RHEL package manager",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install packages"},
|
||||||
|
{Name: "remove", Description: "remove packages", Generator: installedPackageGenerator("dnf")},
|
||||||
|
{Name: "update", Description: "update packages"},
|
||||||
|
{Name: "upgrade", Description: "upgrade system"},
|
||||||
|
{Name: "search", Description: "search packages"},
|
||||||
|
{Name: "info", Description: "show package info"},
|
||||||
|
{Name: "list", Description: "list packages"},
|
||||||
|
{Name: "autoremove", Description: "remove unneeded"},
|
||||||
|
{Name: "clean", Description: "clean cache", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "all", Description: "clean all"},
|
||||||
|
{Name: "packages", Description: "clean packages"},
|
||||||
|
{Name: "metadata", Description: "clean metadata"},
|
||||||
|
}},
|
||||||
|
{Name: "repolist", Description: "list repos"},
|
||||||
|
{Name: "history", Description: "transaction history"},
|
||||||
|
{Name: "group", Description: "manage package groups"},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-y", Description: "yes to all"},
|
||||||
|
{Name: "-q", Description: "quiet"},
|
||||||
|
{Name: "--best", Description: "install best"},
|
||||||
|
{Name: "--allowerasing", Description: "allow package replacement"},
|
||||||
|
{Name: "--no-best", Description: "allow older versions"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// yum (RHEL/CentOS legacy)
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "yum",
|
||||||
|
Description: "RHEL/CentOS package manager (legacy)",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install packages"},
|
||||||
|
{Name: "remove", Description: "remove packages", Generator: installedPackageGenerator("yum")},
|
||||||
|
{Name: "update", Description: "update packages"},
|
||||||
|
{Name: "search", Description: "search packages"},
|
||||||
|
{Name: "info", Description: "show package info"},
|
||||||
|
{Name: "list", Description: "list packages"},
|
||||||
|
{Name: "clean", Description: "clean cache"},
|
||||||
|
{Name: "repolist", Description: "list repos"},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-y", Description: "yes to all"},
|
||||||
|
{Name: "-q", Description: "quiet"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// brew (macOS / Linux Homebrew)
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "brew",
|
||||||
|
Description: "Homebrew package manager",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install formula/cask"},
|
||||||
|
{Name: "uninstall", Description: "remove formula", Generator: installedPackageGenerator("brew")},
|
||||||
|
{Name: "reinstall", Description: "reinstall formula", Generator: installedPackageGenerator("brew")},
|
||||||
|
{Name: "update", Description: "update Homebrew"},
|
||||||
|
{Name: "upgrade", Description: "upgrade packages"},
|
||||||
|
{Name: "search", Description: "search formulas"},
|
||||||
|
{Name: "info", Description: "show package info"},
|
||||||
|
{Name: "list", Description: "list installed"},
|
||||||
|
{Name: "outdated", Description: "list outdated"},
|
||||||
|
{Name: "doctor", Description: "check system"},
|
||||||
|
{Name: "cleanup", Description: "remove old versions"},
|
||||||
|
{Name: "tap", Description: "add tap repo"},
|
||||||
|
{Name: "untap", Description: "remove tap"},
|
||||||
|
{Name: "services", Description: "manage services", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "start", Description: "start service"},
|
||||||
|
{Name: "stop", Description: "stop service"},
|
||||||
|
{Name: "restart", Description: "restart service"},
|
||||||
|
{Name: "list", Description: "list services"},
|
||||||
|
}},
|
||||||
|
{Name: "cask", Description: "manage GUI apps", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install cask"},
|
||||||
|
{Name: "uninstall", Description: "remove cask"},
|
||||||
|
{Name: "list", Description: "list casks"},
|
||||||
|
}},
|
||||||
|
{Name: "pin", Description: "pin formula version", Generator: installedPackageGenerator("brew")},
|
||||||
|
{Name: "unpin", Description: "unpin formula", Generator: installedPackageGenerator("brew")},
|
||||||
|
{Name: "link", Description: "create symlinks", Generator: installedPackageGenerator("brew")},
|
||||||
|
{Name: "unlink", Description: "remove symlinks", Generator: installedPackageGenerator("brew")},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "--cask", Description: "target GUI app"},
|
||||||
|
{Name: "--formula", Description: "target formula"},
|
||||||
|
{Name: "--force", Description: "force operation"},
|
||||||
|
{Name: "--verbose", Description: "verbose"},
|
||||||
|
{Name: "--dry-run", Description: "simulate"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// snap
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "snap",
|
||||||
|
Description: "snap package manager",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install snap"},
|
||||||
|
{Name: "remove", Description: "remove snap"},
|
||||||
|
{Name: "refresh", Description: "update snaps"},
|
||||||
|
{Name: "list", Description: "list installed"},
|
||||||
|
{Name: "find", Description: "search snaps"},
|
||||||
|
{Name: "info", Description: "show snap info"},
|
||||||
|
{Name: "run", Description: "run snap app"},
|
||||||
|
{Name: "connect", Description: "connect snap plug"},
|
||||||
|
{Name: "disconnect", Description: "disconnect snap plug"},
|
||||||
|
{Name: "revert", Description: "revert to previous"},
|
||||||
|
{Name: "enable", Description: "enable snap"},
|
||||||
|
{Name: "disable", Description: "disable snap"},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "--classic", Description: "classic confinement"},
|
||||||
|
{Name: "--beta", Description: "beta channel"},
|
||||||
|
{Name: "--edge", Description: "edge channel"},
|
||||||
|
{Name: "--channel", Description: "specific channel"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// flatpak
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "flatpak",
|
||||||
|
Description: "flatpak package manager",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install application"},
|
||||||
|
{Name: "uninstall", Description: "remove application"},
|
||||||
|
{Name: "update", Description: "update applications"},
|
||||||
|
{Name: "list", Description: "list installed"},
|
||||||
|
{Name: "search", Description: "search applications"},
|
||||||
|
{Name: "info", Description: "show app info"},
|
||||||
|
{Name: "run", Description: "run application"},
|
||||||
|
{Name: "remote-add", Description: "add remote"},
|
||||||
|
{Name: "remote-delete", Description: "delete remote"},
|
||||||
|
{Name: "remote-list", Description: "list remotes"},
|
||||||
|
{Name: "repair", Description: "repair installation"},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "--user", Description: "user installation"},
|
||||||
|
{Name: "--system", Description: "system installation"},
|
||||||
|
{Name: "-y", Description: "non-interactive"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package python
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pipPackageGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, "pip", "list", "--format=freeze")
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
// try pip3
|
||||||
|
cmd = exec.CommandContext(ctx, "pip3", "list", "--format=freeze")
|
||||||
|
out, err = cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []core.Suggestion
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// format is "package==version"
|
||||||
|
parts := strings.SplitN(line, "==", 2)
|
||||||
|
name := parts[0]
|
||||||
|
desc := ""
|
||||||
|
if len(parts) == 2 {
|
||||||
|
desc = "v" + parts[1]
|
||||||
|
}
|
||||||
|
results = append(results, core.Suggestion{Cmd: name, Desc: desc})
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePipSpec(name string) *core.Spec {
|
||||||
|
return &core.Spec{
|
||||||
|
Name: name,
|
||||||
|
Description: "python packages",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install package", Options: []core.Option{
|
||||||
|
{Name: "-r", Description: "from requirements file"},
|
||||||
|
{Name: "-U", Description: "upgrade"},
|
||||||
|
{Name: "--user", Description: "install for user only"},
|
||||||
|
{Name: "--editable", Description: "editable install"},
|
||||||
|
{Name: "--index-url", Description: "custom package index"},
|
||||||
|
{Name: "--no-deps", Description: "skip dependencies"},
|
||||||
|
}},
|
||||||
|
{Name: "uninstall", Description: "remove package", Generator: pipPackageGenerator, Options: []core.Option{
|
||||||
|
{Name: "-y", Description: "yes to all prompts"},
|
||||||
|
}},
|
||||||
|
{Name: "freeze", Description: "list installed packages"},
|
||||||
|
{Name: "list", Description: "list installed", Options: []core.Option{
|
||||||
|
{Name: "--outdated", Description: "show outdated"},
|
||||||
|
{Name: "--format", Description: "output format"},
|
||||||
|
}},
|
||||||
|
{Name: "show", Description: "show package info", Generator: pipPackageGenerator},
|
||||||
|
{Name: "download", Description: "download packages"},
|
||||||
|
{Name: "check", Description: "verify compatibility"},
|
||||||
|
{Name: "config", Description: "manage config"},
|
||||||
|
{Name: "search", Description: "search pypi"},
|
||||||
|
{Name: "wheel", Description: "build wheel archive"},
|
||||||
|
{Name: "hash", Description: "compute hashes"},
|
||||||
|
{Name: "completion", Description: "generate completion"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(makePipSpec("pip"))
|
||||||
|
core.Register(makePipSpec("pip3"))
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package python
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makePythonSpec(name string) *core.Spec {
|
||||||
|
return &core.Spec{
|
||||||
|
Name: name,
|
||||||
|
Description: "python interpreter",
|
||||||
|
Generator: core.FileGenerator(".py"),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-m", Description: "run module as script"},
|
||||||
|
{Name: "-c", Description: "run inline command"},
|
||||||
|
{Name: "-i", Description: "interactive after script"},
|
||||||
|
{Name: "-u", Description: "unbuffered stdio"},
|
||||||
|
{Name: "-O", Description: "optimize bytecode"},
|
||||||
|
{Name: "-v", Description: "verbose"},
|
||||||
|
{Name: "-W", Description: "warning control"},
|
||||||
|
{Name: "-B", Description: "no .pyc files"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(makePythonSpec("python"))
|
||||||
|
core.Register(makePythonSpec("python3"))
|
||||||
|
core.Register(makePythonSpec("py"))
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "uv",
|
||||||
|
Description: "fast python package manager",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "add", Description: "add dependency", Options: []core.Option{
|
||||||
|
{Name: "--dev", Description: "dev dependency"},
|
||||||
|
{Name: "--optional", Description: "optional dependency"},
|
||||||
|
{Name: "-e", Description: "editable install"},
|
||||||
|
{Name: "--index-url", Description: "custom index"},
|
||||||
|
}},
|
||||||
|
{Name: "remove", Description: "remove dependency", Generator: pipPackageGenerator},
|
||||||
|
{Name: "sync", Description: "sync environment", Options: []core.Option{
|
||||||
|
{Name: "--frozen", Description: "no lockfile update"},
|
||||||
|
{Name: "--dev", Description: "include dev deps"},
|
||||||
|
}},
|
||||||
|
{Name: "run", Description: "run command in env", Generator: core.FileGenerator(".py")},
|
||||||
|
{Name: "pip", Description: "pip interface", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install packages"},
|
||||||
|
{Name: "uninstall", Description: "uninstall packages", Generator: pipPackageGenerator},
|
||||||
|
{Name: "list", Description: "list packages"},
|
||||||
|
{Name: "freeze", Description: "freeze packages"},
|
||||||
|
{Name: "show", Description: "show package", Generator: pipPackageGenerator},
|
||||||
|
{Name: "compile", Description: "compile requirements"},
|
||||||
|
}},
|
||||||
|
{Name: "venv", Description: "create virtual env", Generator: core.FileGenerator("/"), Options: []core.Option{
|
||||||
|
{Name: "--python", Description: "python version"},
|
||||||
|
{Name: "--seed", Description: "install seed packages"},
|
||||||
|
}},
|
||||||
|
{Name: "init", Description: "init project"},
|
||||||
|
{Name: "build", Description: "build package"},
|
||||||
|
{Name: "publish", Description: "publish to pypi"},
|
||||||
|
{Name: "lock", Description: "update lockfile"},
|
||||||
|
{Name: "tree", Description: "show dependency tree"},
|
||||||
|
{Name: "tool", Description: "manage tools", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install tool"},
|
||||||
|
{Name: "run", Description: "run tool"},
|
||||||
|
{Name: "list", Description: "list tools"},
|
||||||
|
{Name: "uninstall", Description: "uninstall tool"},
|
||||||
|
}},
|
||||||
|
{Name: "python", Description: "manage python versions", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "install", Description: "install python"},
|
||||||
|
{Name: "list", Description: "list versions"},
|
||||||
|
{Name: "find", Description: "find python"},
|
||||||
|
{Name: "pin", Description: "pin python version"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "poetry",
|
||||||
|
Description: "python dependency manager",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "add", Description: "add dependency", Options: []core.Option{
|
||||||
|
{Name: "--dev", Description: "dev dependency"},
|
||||||
|
{Name: "--group", Description: "dependency group"},
|
||||||
|
}},
|
||||||
|
{Name: "remove", Description: "remove dependency", Generator: pipPackageGenerator},
|
||||||
|
{Name: "install", Description: "install dependencies"},
|
||||||
|
{Name: "update", Description: "update dependencies"},
|
||||||
|
{Name: "run", Description: "run in env", Generator: core.FileGenerator(".py")},
|
||||||
|
{Name: "shell", Description: "activate virtual env"},
|
||||||
|
{Name: "build", Description: "build package"},
|
||||||
|
{Name: "publish", Description: "publish to pypi"},
|
||||||
|
{Name: "show", Description: "show packages"},
|
||||||
|
{Name: "check", Description: "check pyproject.toml"},
|
||||||
|
{Name: "init", Description: "init new project"},
|
||||||
|
{Name: "new", Description: "create new project"},
|
||||||
|
{Name: "env", Description: "manage envs", Subcommands: []core.Subcommand{
|
||||||
|
{Name: "info", Description: "show env info"},
|
||||||
|
{Name: "list", Description: "list envs"},
|
||||||
|
{Name: "use", Description: "switch python"},
|
||||||
|
{Name: "remove", Description: "remove env"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package dev
|
package rust
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/versenilvis/iris/commands/core"
|
"github.com/versenilvis/iris/commands/core"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package rust
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "rustc",
|
||||||
|
Description: "Rust compiler",
|
||||||
|
Generator: core.FileGenerator(".rs"),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-o", Description: "output file"},
|
||||||
|
{Name: "--edition", Description: "Rust edition (2015/2018/2021/2024)"},
|
||||||
|
{Name: "--crate-type", Description: "crate type (bin/lib/dylib/cdylib/rlib)"},
|
||||||
|
{Name: "--crate-name", Description: "crate name"},
|
||||||
|
{Name: "-C", Description: "codegen option"},
|
||||||
|
{Name: "-L", Description: "add to library search path"},
|
||||||
|
{Name: "--extern", Description: "extern crate"},
|
||||||
|
{Name: "-g", Description: "debug symbols"},
|
||||||
|
{Name: "-O", Description: "optimize"},
|
||||||
|
{Name: "--emit", Description: "output type (asm/llvm-ir/obj/mir)"},
|
||||||
|
{Name: "--explain", Description: "explain error code"},
|
||||||
|
{Name: "--test", Description: "build test harness"},
|
||||||
|
{Name: "-W", Description: "set lint warning"},
|
||||||
|
{Name: "-A", Description: "set lint allow"},
|
||||||
|
{Name: "-D", Description: "set lint deny"},
|
||||||
|
{Name: "--verbose", Description: "verbose"},
|
||||||
|
{Name: "--version", Description: "print version"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package search
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/versenilvis/iris/commands/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
core.Register(&core.Spec{
|
|
||||||
Name: "find",
|
|
||||||
Description: "search for files",
|
|
||||||
Generator: core.FileGenerator("/"),
|
|
||||||
Options: []core.Option{
|
|
||||||
{Name: "-name", Description: "match by name"},
|
|
||||||
{Name: "-type", Description: "match by type (f/d)"},
|
|
||||||
{Name: "-size", Description: "match by size"},
|
|
||||||
{Name: "-mtime", Description: "match by modified time"},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package search
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/versenilvis/iris/commands/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
core.Register(&core.Spec{
|
|
||||||
Name: "grep",
|
|
||||||
Description: "search text in files",
|
|
||||||
Generator: core.FileGenerator(),
|
|
||||||
Options: []core.Option{
|
|
||||||
{Name: "-r", Description: "recursive"},
|
|
||||||
{Name: "-i", Description: "ignore case"},
|
|
||||||
{Name: "-n", Description: "show line numbers"},
|
|
||||||
{Name: "-v", Description: "invert match"},
|
|
||||||
{Name: "-l", Description: "show filenames only"},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package sys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func envVarGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
var results []core.Suggestion
|
||||||
|
for _, env := range os.Environ() {
|
||||||
|
parts := strings.SplitN(env, "=", 2)
|
||||||
|
name := parts[0]
|
||||||
|
val := ""
|
||||||
|
if len(parts) == 2 {
|
||||||
|
val = parts[1]
|
||||||
|
// truncate long values
|
||||||
|
if len(val) > 60 {
|
||||||
|
val = val[:57] + "..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results = append(results, core.Suggestion{Cmd: name, Desc: val})
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "export",
|
||||||
|
Description: "set environment variable",
|
||||||
|
Generator: envVarGenerator,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "env",
|
||||||
|
Description: "print environment",
|
||||||
|
Generator: envVarGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-i", Description: "start with empty env"},
|
||||||
|
{Name: "-u", Description: "unset variable"},
|
||||||
|
{Name: "-0", Description: "null delimited"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "printenv",
|
||||||
|
Description: "print environment variables",
|
||||||
|
Generator: envVarGenerator,
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "unset",
|
||||||
|
Description: "unset variable",
|
||||||
|
Generator: envVarGenerator,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package sys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "curl",
|
||||||
|
Description: "transfer data via URL",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-X", Description: "request method (GET/POST/PUT/DELETE)"},
|
||||||
|
{Name: "-H", Description: "request header"},
|
||||||
|
{Name: "-d", Description: "request body data"},
|
||||||
|
{Name: "-o", Description: "output to file"},
|
||||||
|
{Name: "-O", Description: "save with remote filename"},
|
||||||
|
{Name: "-s", Description: "silent mode"},
|
||||||
|
{Name: "-S", Description: "show error in silent"},
|
||||||
|
{Name: "-L", Description: "follow redirects"},
|
||||||
|
{Name: "-k", Description: "insecure (skip TLS verify)"},
|
||||||
|
{Name: "-I", Description: "fetch headers only"},
|
||||||
|
{Name: "-v", Description: "verbose"},
|
||||||
|
{Name: "-u", Description: "user:password"},
|
||||||
|
{Name: "-b", Description: "send cookie"},
|
||||||
|
{Name: "-c", Description: "save cookie to file"},
|
||||||
|
{Name: "-F", Description: "multipart form data"},
|
||||||
|
{Name: "--json", Description: "send JSON body"},
|
||||||
|
{Name: "--compressed", Description: "request compressed response"},
|
||||||
|
{Name: "--max-time", Description: "max time in seconds"},
|
||||||
|
{Name: "--retry", Description: "retry count"},
|
||||||
|
{Name: "-A", Description: "custom user-agent"},
|
||||||
|
{Name: "--proxy", Description: "use proxy"},
|
||||||
|
{Name: "-T", Description: "upload file"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "wget",
|
||||||
|
Description: "non-interactive downloader",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-O", Description: "output filename"},
|
||||||
|
{Name: "-q", Description: "quiet"},
|
||||||
|
{Name: "-v", Description: "verbose"},
|
||||||
|
{Name: "-r", Description: "recursive"},
|
||||||
|
{Name: "-P", Description: "output directory"},
|
||||||
|
{Name: "-c", Description: "continue download"},
|
||||||
|
{Name: "--no-check-certificate", Description: "skip TLS verify"},
|
||||||
|
{Name: "-b", Description: "background"},
|
||||||
|
{Name: "--limit-rate", Description: "limit rate"},
|
||||||
|
{Name: "-U", Description: "custom user-agent"},
|
||||||
|
{Name: "--spider", Description: "check URL only"},
|
||||||
|
{Name: "-np", Description: "no parent directories"},
|
||||||
|
{Name: "-nH", Description: "no host directories"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "nc",
|
||||||
|
Description: "netcat - TCP/UDP tool",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-l", Description: "listen mode"},
|
||||||
|
{Name: "-p", Description: "local port"},
|
||||||
|
{Name: "-z", Description: "port scan mode"},
|
||||||
|
{Name: "-v", Description: "verbose"},
|
||||||
|
{Name: "-u", Description: "UDP mode"},
|
||||||
|
{Name: "-n", Description: "no DNS"},
|
||||||
|
{Name: "-w", Description: "timeout"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "ping",
|
||||||
|
Description: "test network connectivity",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-c", Description: "packet count"},
|
||||||
|
{Name: "-i", Description: "interval"},
|
||||||
|
{Name: "-t", Description: "TTL"},
|
||||||
|
{Name: "-s", Description: "packet size"},
|
||||||
|
{Name: "-q", Description: "quiet output"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "dig",
|
||||||
|
Description: "DNS lookup",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "+short", Description: "short output"},
|
||||||
|
{Name: "+noall", Description: "no output flags"},
|
||||||
|
{Name: "+answer", Description: "answer section only"},
|
||||||
|
{Name: "-t", Description: "record type"},
|
||||||
|
{Name: "@", Description: "nameserver"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "nslookup",
|
||||||
|
Description: "query DNS",
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "ifconfig",
|
||||||
|
Description: "configure network interface",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-a", Description: "all interfaces"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "ip",
|
||||||
|
Description: "show/manage network",
|
||||||
|
Subcommands: []core.Subcommand{
|
||||||
|
{Name: "addr", Description: "address management"},
|
||||||
|
{Name: "link", Description: "network device management"},
|
||||||
|
{Name: "route", Description: "routing table management"},
|
||||||
|
{Name: "neigh", Description: "ARP table"},
|
||||||
|
{Name: "rule", Description: "routing policy"},
|
||||||
|
},
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-4", Description: "IPv4 only"},
|
||||||
|
{Name: "-6", Description: "IPv6 only"},
|
||||||
|
{Name: "-br", Description: "brief output"},
|
||||||
|
{Name: "-c", Description: "colorize"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "netstat",
|
||||||
|
Description: "network statistics",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-t", Description: "TCP connections"},
|
||||||
|
{Name: "-u", Description: "UDP connections"},
|
||||||
|
{Name: "-l", Description: "listening only"},
|
||||||
|
{Name: "-n", Description: "numeric addresses"},
|
||||||
|
{Name: "-p", Description: "show PID"},
|
||||||
|
{Name: "-r", Description: "routing table"},
|
||||||
|
{Name: "-s", Description: "statistics"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "ss",
|
||||||
|
Description: "socket statistics",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-t", Description: "TCP"},
|
||||||
|
{Name: "-u", Description: "UDP"},
|
||||||
|
{Name: "-l", Description: "listening"},
|
||||||
|
{Name: "-n", Description: "numeric"},
|
||||||
|
{Name: "-p", Description: "show process"},
|
||||||
|
{Name: "-a", Description: "all"},
|
||||||
|
{Name: "-4", Description: "IPv4"},
|
||||||
|
{Name: "-6", Description: "IPv6"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package sys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func processGenerator(tokens []string, _ string, _ string) []core.Suggestion {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// output: "pid command"
|
||||||
|
cmd := exec.CommandContext(ctx, "ps", "-eo", "pid,comm")
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var results []core.Suggestion
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pid := parts[0]
|
||||||
|
if pid[0] < '0' || pid[0] > '9' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := parts[1]
|
||||||
|
|
||||||
|
// suggest both PID and process name
|
||||||
|
if !seen[pid] {
|
||||||
|
seen[pid] = true
|
||||||
|
results = append(results, core.Suggestion{Cmd: pid, Desc: name})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "ps",
|
||||||
|
Description: "report processes",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-e", Description: "all processes"},
|
||||||
|
{Name: "-f", Description: "full format"},
|
||||||
|
{Name: "-u", Description: "by user"},
|
||||||
|
{Name: "aux", Description: "all + user + x"},
|
||||||
|
{Name: "-o", Description: "custom format"},
|
||||||
|
{Name: "--pid", Description: "filter by PID"},
|
||||||
|
{Name: "--sort", Description: "sort by field"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "kill",
|
||||||
|
Description: "send signal to process",
|
||||||
|
Generator: processGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-9", Description: "SIGKILL (force)"},
|
||||||
|
{Name: "-15", Description: "SIGTERM (graceful)"},
|
||||||
|
{Name: "-2", Description: "SIGINT"},
|
||||||
|
{Name: "-HUP", Description: "SIGHUP (reload)"},
|
||||||
|
{Name: "-l", Description: "list signal names"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "killall",
|
||||||
|
Description: "kill by process name",
|
||||||
|
Generator: processGenerator,
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-9", Description: "SIGKILL"},
|
||||||
|
{Name: "-s", Description: "specify signal"},
|
||||||
|
{Name: "-u", Description: "only for user"},
|
||||||
|
{Name: "-i", Description: "confirm each"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "pkill",
|
||||||
|
Description: "kill by pattern",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-9", Description: "SIGKILL"},
|
||||||
|
{Name: "-f", Description: "match full command"},
|
||||||
|
{Name: "-u", Description: "match by user"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "pgrep",
|
||||||
|
Description: "find process by pattern",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-l", Description: "list name and PID"},
|
||||||
|
{Name: "-f", Description: "match full command"},
|
||||||
|
{Name: "-u", Description: "match by user"},
|
||||||
|
{Name: "-a", Description: "list command line"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package text
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "find",
|
||||||
|
Description: "search for files",
|
||||||
|
Generator: core.FileGenerator("/"),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-name", Description: "match by name"},
|
||||||
|
{Name: "-iname", Description: "match by name (case insensitive)"},
|
||||||
|
{Name: "-type", Description: "match by type (f=file, d=dir, l=link)"},
|
||||||
|
{Name: "-size", Description: "match by size (+1M, -10k)"},
|
||||||
|
{Name: "-mtime", Description: "modified n days ago"},
|
||||||
|
{Name: "-atime", Description: "accessed n days ago"},
|
||||||
|
{Name: "-ctime", Description: "changed n days ago"},
|
||||||
|
{Name: "-newer", Description: "newer than file"},
|
||||||
|
{Name: "-maxdepth", Description: "max depth to descend"},
|
||||||
|
{Name: "-mindepth", Description: "min depth to start"},
|
||||||
|
{Name: "-exec", Description: "execute command on match"},
|
||||||
|
{Name: "-execdir", Description: "exec in matched dir"},
|
||||||
|
{Name: "-delete", Description: "delete matched files"},
|
||||||
|
{Name: "-print", Description: "print matched paths"},
|
||||||
|
{Name: "-print0", Description: "null-terminated output"},
|
||||||
|
{Name: "-empty", Description: "match empty files/dirs"},
|
||||||
|
{Name: "-perm", Description: "match by permissions"},
|
||||||
|
{Name: "-user", Description: "match by owner"},
|
||||||
|
{Name: "-group", Description: "match by group"},
|
||||||
|
{Name: "-not", Description: "negate expression"},
|
||||||
|
{Name: "-or", Description: "logical OR"},
|
||||||
|
{Name: "-and", Description: "logical AND"},
|
||||||
|
{Name: "-prune", Description: "exclude from search"},
|
||||||
|
{Name: "-ls", Description: "list in ls format"},
|
||||||
|
{Name: "-xdev", Description: "don't cross filesystems"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package text
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "grep",
|
||||||
|
Description: "search text in files",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-r", Description: "recursive"},
|
||||||
|
{Name: "-R", Description: "recursive + follow symlinks"},
|
||||||
|
{Name: "-i", Description: "ignore case"},
|
||||||
|
{Name: "-n", Description: "show line numbers"},
|
||||||
|
{Name: "-v", Description: "invert match"},
|
||||||
|
{Name: "-l", Description: "show filenames only"},
|
||||||
|
{Name: "-L", Description: "files without match"},
|
||||||
|
{Name: "-c", Description: "count matching lines"},
|
||||||
|
{Name: "-E", Description: "extended regex (egrep)"},
|
||||||
|
{Name: "-P", Description: "perl-compatible regex"},
|
||||||
|
{Name: "-F", Description: "fixed string (fgrep)"},
|
||||||
|
{Name: "-w", Description: "whole word match"},
|
||||||
|
{Name: "-x", Description: "whole line match"},
|
||||||
|
{Name: "-A", Description: "lines after match"},
|
||||||
|
{Name: "-B", Description: "lines before match"},
|
||||||
|
{Name: "-C", Description: "lines around match"},
|
||||||
|
{Name: "-m", Description: "max matches per file"},
|
||||||
|
{Name: "-o", Description: "only matching part"},
|
||||||
|
{Name: "-q", Description: "quiet mode"},
|
||||||
|
{Name: "--include", Description: "include file pattern"},
|
||||||
|
{Name: "--exclude", Description: "exclude file pattern"},
|
||||||
|
{Name: "--exclude-dir", Description: "exclude directory"},
|
||||||
|
{Name: "-H", Description: "always print filename"},
|
||||||
|
{Name: "-h", Description: "never print filename"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// egrep and fgrep aliases
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "egrep",
|
||||||
|
Description: "grep with extended regex",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-r", Description: "recursive"},
|
||||||
|
{Name: "-i", Description: "ignore case"},
|
||||||
|
{Name: "-n", Description: "show line numbers"},
|
||||||
|
{Name: "-v", Description: "invert match"},
|
||||||
|
{Name: "-l", Description: "show filenames only"},
|
||||||
|
{Name: "-w", Description: "whole word"},
|
||||||
|
{Name: "-A", Description: "lines after"},
|
||||||
|
{Name: "-B", Description: "lines before"},
|
||||||
|
{Name: "-C", Description: "lines around"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package text
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "rg",
|
||||||
|
Description: "ripgrep (fast search)",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-i", Description: "case insensitive"},
|
||||||
|
{Name: "-s", Description: "case sensitive"},
|
||||||
|
{Name: "-n", Description: "show line numbers"},
|
||||||
|
{Name: "-N", Description: "hide line numbers"},
|
||||||
|
{Name: "-l", Description: "filenames only"},
|
||||||
|
{Name: "-L", Description: "follow symlinks"},
|
||||||
|
{Name: "-c", Description: "count matches"},
|
||||||
|
{Name: "-w", Description: "word boundary match"},
|
||||||
|
{Name: "-x", Description: "whole line match"},
|
||||||
|
{Name: "-v", Description: "invert match"},
|
||||||
|
{Name: "-o", Description: "only matching text"},
|
||||||
|
{Name: "-A", Description: "lines after match"},
|
||||||
|
{Name: "-B", Description: "lines before match"},
|
||||||
|
{Name: "-C", Description: "lines around match"},
|
||||||
|
{Name: "-m", Description: "max matches per file"},
|
||||||
|
{Name: "-e", Description: "additional pattern"},
|
||||||
|
{Name: "-f", Description: "patterns from file"},
|
||||||
|
{Name: "--type", Description: "file type filter (e.g. go, py, js)"},
|
||||||
|
{Name: "--type-not", Description: "exclude file type"},
|
||||||
|
{Name: "--glob", Description: "include glob pattern"},
|
||||||
|
{Name: "--iglob", Description: "include glob (case insensitive)"},
|
||||||
|
{Name: "--hidden", Description: "search hidden files"},
|
||||||
|
{Name: "--no-ignore", Description: "don't use .gitignore"},
|
||||||
|
{Name: "--no-ignore-vcs", Description: "don't use VCS ignore"},
|
||||||
|
{Name: "-g", Description: "include/exclude glob"},
|
||||||
|
{Name: "--json", Description: "output as JSON"},
|
||||||
|
{Name: "--stats", Description: "print match statistics"},
|
||||||
|
{Name: "--color", Description: "colorize output (auto/always/never)"},
|
||||||
|
{Name: "--no-heading", Description: "no file name headings"},
|
||||||
|
{Name: "-H", Description: "always show filename"},
|
||||||
|
{Name: "--max-depth", Description: "max directory depth"},
|
||||||
|
{Name: "-p", Description: "smart heading/colors (for piping)"},
|
||||||
|
{Name: "-U", Description: "multiline matching"},
|
||||||
|
{Name: "--multiline", Description: "enable multiline"},
|
||||||
|
{Name: "-F", Description: "treat pattern as literal string"},
|
||||||
|
{Name: "--fixed-strings", Description: "literal string match"},
|
||||||
|
{Name: "-z", Description: "search compressed files"},
|
||||||
|
{Name: "--encoding", Description: "file encoding"},
|
||||||
|
{Name: "--trim", Description: "trim whitespace"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "fd",
|
||||||
|
Description: "fast find alternative",
|
||||||
|
Generator: core.FileGenerator("/"),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-t", Description: "type (f/d/l/x/e/s)"},
|
||||||
|
{Name: "-e", Description: "file extension"},
|
||||||
|
{Name: "-H", Description: "include hidden"},
|
||||||
|
{Name: "-I", Description: "no gitignore"},
|
||||||
|
{Name: "-d", Description: "max depth"},
|
||||||
|
{Name: "-x", Description: "execute command"},
|
||||||
|
{Name: "-X", Description: "exec batch"},
|
||||||
|
{Name: "-l", Description: "long listing"},
|
||||||
|
{Name: "-L", Description: "follow symlinks"},
|
||||||
|
{Name: "-p", Description: "full path match"},
|
||||||
|
{Name: "-g", Description: "glob pattern"},
|
||||||
|
{Name: "-c", Description: "color mode"},
|
||||||
|
{Name: "-0", Description: "null-separated output"},
|
||||||
|
{Name: "-j", Description: "number of threads"},
|
||||||
|
{Name: "--changed-within", Description: "recently changed"},
|
||||||
|
{Name: "--changed-before", Description: "changed before date"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package text
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// awk
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "awk",
|
||||||
|
Description: "pattern-directed scanning",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-F", Description: "field separator"},
|
||||||
|
{Name: "-v", Description: "assign variable"},
|
||||||
|
{Name: "-f", Description: "read program from file"},
|
||||||
|
{Name: "-i", Description: "in-place edit (gawk)"},
|
||||||
|
{Name: "--posix", Description: "POSIX compat mode"},
|
||||||
|
{Name: "-W", Description: "compatibility options"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// gawk is also common
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "gawk",
|
||||||
|
Description: "GNU awk",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-F", Description: "field separator"},
|
||||||
|
{Name: "-v", Description: "assign variable"},
|
||||||
|
{Name: "-f", Description: "read program from file"},
|
||||||
|
{Name: "-i", Description: "in-place edit"},
|
||||||
|
{Name: "--sandbox", Description: "sandbox mode"},
|
||||||
|
{Name: "--profile", Description: "profiling output"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// sed
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "sed",
|
||||||
|
Description: "stream editor",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-e", Description: "add expression"},
|
||||||
|
{Name: "-f", Description: "read script from file"},
|
||||||
|
{Name: "-i", Description: "in-place edit"},
|
||||||
|
{Name: "-n", Description: "suppress default output"},
|
||||||
|
{Name: "-E", Description: "extended regex"},
|
||||||
|
{Name: "-r", Description: "extended regex (GNU)"},
|
||||||
|
{Name: "-z", Description: "null-delimited lines"},
|
||||||
|
{Name: "--sandbox", Description: "sandbox mode"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// xargs
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "xargs",
|
||||||
|
Description: "build and run commands from stdin",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-I", Description: "replace string (e.g. -I{})"},
|
||||||
|
{Name: "-n", Description: "max args per command"},
|
||||||
|
{Name: "-P", Description: "parallel jobs"},
|
||||||
|
{Name: "-0", Description: "null-delimited input"},
|
||||||
|
{Name: "-d", Description: "custom delimiter"},
|
||||||
|
{Name: "-t", Description: "print command before executing"},
|
||||||
|
{Name: "-p", Description: "prompt before executing"},
|
||||||
|
{Name: "-r", Description: "no run if empty input"},
|
||||||
|
{Name: "--no-run-if-empty", Description: "don't run if no input"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// tr
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "tr",
|
||||||
|
Description: "translate or delete characters",
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-d", Description: "delete characters"},
|
||||||
|
{Name: "-s", Description: "squeeze repeated chars"},
|
||||||
|
{Name: "-c", Description: "complement set"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// sort
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "sort",
|
||||||
|
Description: "sort lines of text",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-n", Description: "numeric sort"},
|
||||||
|
{Name: "-r", Description: "reverse sort"},
|
||||||
|
{Name: "-k", Description: "sort by key field"},
|
||||||
|
{Name: "-t", Description: "field separator"},
|
||||||
|
{Name: "-u", Description: "unique (remove duplicates)"},
|
||||||
|
{Name: "-f", Description: "ignore case"},
|
||||||
|
{Name: "-h", Description: "human-readable sort"},
|
||||||
|
{Name: "-V", Description: "version sort"},
|
||||||
|
{Name: "-o", Description: "output file"},
|
||||||
|
{Name: "--parallel", Description: "parallel sort"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// uniq
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "uniq",
|
||||||
|
Description: "filter adjacent duplicate lines",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-c", Description: "prefix count"},
|
||||||
|
{Name: "-d", Description: "only duplicates"},
|
||||||
|
{Name: "-u", Description: "only unique"},
|
||||||
|
{Name: "-i", Description: "ignore case"},
|
||||||
|
{Name: "-f", Description: "skip n fields"},
|
||||||
|
{Name: "-s", Description: "skip n chars"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// cut
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "cut",
|
||||||
|
Description: "extract columns from lines",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-d", Description: "delimiter"},
|
||||||
|
{Name: "-f", Description: "field numbers (e.g. 1,3 or 1-3)"},
|
||||||
|
{Name: "-c", Description: "character positions"},
|
||||||
|
{Name: "--output-delimiter", Description: "output delimiter"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// tee
|
||||||
|
core.Register(&core.Spec{
|
||||||
|
Name: "tee",
|
||||||
|
Description: "read stdin, write to stdout and files",
|
||||||
|
Generator: core.FileGenerator(),
|
||||||
|
Options: []core.Option{
|
||||||
|
{Name: "-a", Description: "append to files"},
|
||||||
|
{Name: "-i", Description: "ignore interrupts"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package info
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/versenilvis/iris/commands/core"
|
"github.com/versenilvis/iris/commands/core"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package info
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/versenilvis/iris/commands/core"
|
"github.com/versenilvis/iris/commands/core"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package info
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/versenilvis/iris/commands/core"
|
"github.com/versenilvis/iris/commands/core"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package info
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/versenilvis/iris/commands/core"
|
"github.com/versenilvis/iris/commands/core"
|
||||||
@@ -107,6 +107,9 @@ func SearchHistory(query string) ([]HistResult, error) {
|
|||||||
allCmds = append(allCmds, cmd)
|
allCmds = append(allCmds, cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// build historyCache backwards so newest commands come first
|
// build historyCache backwards so newest commands come first
|
||||||
seen := make(map[string]bool)
|
seen := make(map[string]bool)
|
||||||
|
|||||||
@@ -99,7 +99,9 @@ func (o *Overlay) RenderGhostText(buffer string, userNavigated bool) string {
|
|||||||
if ghostText != "" || padLen > 0 {
|
if ghostText != "" || padLen > 0 {
|
||||||
s.WriteString("\0337") // SAVE CURSOR at prompt
|
s.WriteString("\0337") // SAVE CURSOR at prompt
|
||||||
if ghostText != "" {
|
if ghostText != "" {
|
||||||
s.WriteString("\033[90m" + ghostText + "\033[0m")
|
s.WriteString("\033[90m")
|
||||||
|
s.WriteString(ghostText)
|
||||||
|
s.WriteString("\033[0m")
|
||||||
}
|
}
|
||||||
if padLen > 0 {
|
if padLen > 0 {
|
||||||
s.WriteString(strings.Repeat(" ", padLen))
|
s.WriteString(strings.Repeat(" ", padLen))
|
||||||
|
|||||||
+1
-1
@@ -177,6 +177,6 @@ check-interval = "24h"
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("\nSetup complete! Please restart your terminal or run:")
|
fmt.Println("\nSetup complete! Please restart your terminal or run:")
|
||||||
fmt.Printf(" source %s\n", configFile)
|
fmt.Printf(" \033[32msource %s\033[0m\n", configFile)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-6
@@ -13,12 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/versenilvis/iris/commands/core"
|
"github.com/versenilvis/iris/commands/core"
|
||||||
_ "github.com/versenilvis/iris/commands/dev"
|
_ "github.com/versenilvis/iris/commands"
|
||||||
_ "github.com/versenilvis/iris/commands/fs"
|
|
||||||
_ "github.com/versenilvis/iris/commands/info"
|
|
||||||
_ "github.com/versenilvis/iris/commands/runner"
|
|
||||||
_ "github.com/versenilvis/iris/commands/search"
|
|
||||||
_ "github.com/versenilvis/iris/commands/view"
|
|
||||||
"github.com/versenilvis/iris/config"
|
"github.com/versenilvis/iris/config"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -276,6 +276,9 @@ func runWrapper() {
|
|||||||
rBuf.WriteString(overlay.Render())
|
rBuf.WriteString(overlay.Render())
|
||||||
_, _ = os.Stdout.Write([]byte(rBuf.String()))
|
_, _ = os.Stdout.Write([]byte(rBuf.String()))
|
||||||
}
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
debugLog("[IPC] scanner error: %v", err)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var naiveBuffer string
|
var naiveBuffer string
|
||||||
|
|||||||
+70
-1
@@ -9,7 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/versenilvis/iris/commands/core"
|
"github.com/versenilvis/iris/commands/core"
|
||||||
_ "github.com/versenilvis/iris/commands/dev"
|
_ "github.com/versenilvis/iris/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
// setupGitRepo creates a real git repo in a temp dir with:
|
// setupGitRepo creates a real git repo in a temp dir with:
|
||||||
@@ -304,4 +304,73 @@ func TestGitSuggestions(t *testing.T) {
|
|||||||
t.Error("git reset HEAD should suggest file.go")
|
t.Error("git reset HEAD should suggest file.go")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("show suggests tags and commits", func(t *testing.T) {
|
||||||
|
res := core.Lookup("git show ")
|
||||||
|
foundTag := false
|
||||||
|
foundCommit := false
|
||||||
|
for _, r := range res {
|
||||||
|
if strings.Contains(r.Cmd, "v1.0") {
|
||||||
|
foundTag = true
|
||||||
|
}
|
||||||
|
// commit hashes are 7+ hex chars
|
||||||
|
parts := strings.Fields(r.Cmd)
|
||||||
|
if len(parts) > 0 {
|
||||||
|
h := parts[len(parts)-1]
|
||||||
|
if len(h) >= 7 {
|
||||||
|
foundCommit = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundTag {
|
||||||
|
t.Error("git show should suggest tag v1.0")
|
||||||
|
}
|
||||||
|
if !foundCommit {
|
||||||
|
t.Error("git show should suggest commit hashes")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cherry-pick suggests commits", func(t *testing.T) {
|
||||||
|
res := core.Lookup("git cherry-pick ")
|
||||||
|
if len(res) == 0 {
|
||||||
|
t.Error("git cherry-pick should suggest commits")
|
||||||
|
}
|
||||||
|
// all suggestions should be short hex hashes
|
||||||
|
for _, r := range res {
|
||||||
|
parts := strings.Fields(r.Cmd)
|
||||||
|
if len(parts) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
h := parts[len(parts)-1]
|
||||||
|
if len(h) < 7 {
|
||||||
|
t.Errorf("cherry-pick suggestion looks invalid: %s", r.Cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("revert suggests commits", func(t *testing.T) {
|
||||||
|
res := core.Lookup("git revert ")
|
||||||
|
if len(res) == 0 {
|
||||||
|
t.Error("git revert should suggest commits")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("global flags don't break subcommand detection", func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
out, err := exec.CommandContext(ctx, "git", "rev-parse", "--abbrev-ref", "HEAD").Output()
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("can't determine HEAD branch")
|
||||||
|
}
|
||||||
|
activeBranch := strings.TrimSpace(string(out))
|
||||||
|
|
||||||
|
res := core.Lookup("git -c core.pager=cat checkout ")
|
||||||
|
for _, r := range res {
|
||||||
|
parts := strings.Fields(r.Cmd)
|
||||||
|
for _, p := range parts {
|
||||||
|
if p == activeBranch {
|
||||||
|
t.Errorf("git -c core.pager=cat checkout should not suggest active branch '%s', got: %s", activeBranch, r.Cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/versenilvis/iris/commands/core"
|
||||||
|
"github.com/versenilvis/iris/commands/js"
|
||||||
|
_ "github.com/versenilvis/iris/commands"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNpmScriptGenerator(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
_ = os.Chdir(tmp)
|
||||||
|
|
||||||
|
t.Run("reads scripts from package.json", func(t *testing.T) {
|
||||||
|
pkg := map[string]any{
|
||||||
|
"name": "test-app",
|
||||||
|
"scripts": map[string]string{
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"test": "vitest",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
data, _ := json.Marshal(pkg)
|
||||||
|
_ = os.WriteFile(filepath.Join(tmp, "package.json"), data, 0644)
|
||||||
|
defer os.Remove(filepath.Join(tmp, "package.json"))
|
||||||
|
|
||||||
|
// ensure CWD is tmp
|
||||||
|
core.ShellPID = 0
|
||||||
|
_ = os.Chdir(tmp)
|
||||||
|
|
||||||
|
results := js.NpmScriptGenerator(nil, "", "")
|
||||||
|
|
||||||
|
found := make(map[string]bool)
|
||||||
|
for _, r := range results {
|
||||||
|
found[r.Cmd] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, expected := range []string{"dev", "build", "test", "lint", "preview", "typecheck"} {
|
||||||
|
if !found[expected] {
|
||||||
|
t.Errorf("expected script '%s' in suggestions", expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("priority scripts come first", func(t *testing.T) {
|
||||||
|
pkg := map[string]any{
|
||||||
|
"scripts": map[string]string{
|
||||||
|
"zzz-last": "echo last",
|
||||||
|
"dev": "vite",
|
||||||
|
"aaa-first": "echo first",
|
||||||
|
"build": "vite build",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
data, _ := json.Marshal(pkg)
|
||||||
|
_ = os.WriteFile(filepath.Join(tmp, "package.json"), data, 0644)
|
||||||
|
defer os.Remove(filepath.Join(tmp, "package.json"))
|
||||||
|
|
||||||
|
_ = os.Chdir(tmp)
|
||||||
|
results := js.NpmScriptGenerator(nil, "", "")
|
||||||
|
|
||||||
|
if len(results) < 2 {
|
||||||
|
t.Fatal("expected at least 2 results")
|
||||||
|
}
|
||||||
|
|
||||||
|
// dev should appear before zzz-last
|
||||||
|
devIdx, zzzIdx := -1, -1
|
||||||
|
for i, r := range results {
|
||||||
|
if r.Cmd == "dev" {
|
||||||
|
devIdx = i
|
||||||
|
}
|
||||||
|
if r.Cmd == "zzz-last" {
|
||||||
|
zzzIdx = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if devIdx == -1 {
|
||||||
|
t.Error("dev not found")
|
||||||
|
}
|
||||||
|
if zzzIdx == -1 {
|
||||||
|
t.Error("zzz-last not found")
|
||||||
|
}
|
||||||
|
if devIdx > zzzIdx {
|
||||||
|
t.Errorf("'dev' (idx %d) should come before 'zzz-last' (idx %d)", devIdx, zzzIdx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fallback when no package.json", func(t *testing.T) {
|
||||||
|
emptyDir := t.TempDir()
|
||||||
|
_ = os.Chdir(emptyDir)
|
||||||
|
defer func() { _ = os.Chdir(tmp) }()
|
||||||
|
|
||||||
|
results := js.NpmScriptGenerator(nil, "", "")
|
||||||
|
if len(results) == 0 {
|
||||||
|
t.Error("expected fallback suggestions when no package.json")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSshHostGenerator(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
sshDir := filepath.Join(tmp, ".ssh")
|
||||||
|
_ = os.MkdirAll(sshDir, 0700)
|
||||||
|
|
||||||
|
configContent := `
|
||||||
|
Host prod-server
|
||||||
|
HostName 10.0.0.1
|
||||||
|
User deploy
|
||||||
|
|
||||||
|
Host staging bastion
|
||||||
|
HostName staging.example.com
|
||||||
|
User ubuntu
|
||||||
|
|
||||||
|
Host *.internal
|
||||||
|
User admin
|
||||||
|
|
||||||
|
Host !forbidden wildcard-test
|
||||||
|
HostName test.internal
|
||||||
|
`
|
||||||
|
_ = os.WriteFile(filepath.Join(sshDir, "config"), []byte(configContent), 0600)
|
||||||
|
|
||||||
|
// temporarily replace home dir lookup by using a mock path
|
||||||
|
// we call the generator directly with a custom home dir
|
||||||
|
results := sshHostGeneratorFromPath(filepath.Join(sshDir, "config"))
|
||||||
|
|
||||||
|
found := make(map[string]bool)
|
||||||
|
for _, r := range results {
|
||||||
|
found[r.Cmd] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found["prod-server"] {
|
||||||
|
t.Error("expected prod-server in suggestions")
|
||||||
|
}
|
||||||
|
if !found["staging"] {
|
||||||
|
t.Error("expected staging in suggestions")
|
||||||
|
}
|
||||||
|
if !found["bastion"] {
|
||||||
|
t.Error("expected bastion in suggestions")
|
||||||
|
}
|
||||||
|
|
||||||
|
// wildcards should be excluded
|
||||||
|
if found["*.internal"] {
|
||||||
|
t.Error("wildcard *.internal should not be suggested")
|
||||||
|
}
|
||||||
|
if found["!forbidden"] {
|
||||||
|
t.Error("negated host !forbidden should not be suggested")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sshHostGeneratorFromPath is a helper that reads a specific ssh config path
|
||||||
|
func sshHostGeneratorFromPath(configPath string) []core.Suggestion {
|
||||||
|
import_bufio := func() {
|
||||||
|
// using bufio in the same style as ssh.go
|
||||||
|
}
|
||||||
|
_ = import_bufio
|
||||||
|
|
||||||
|
f, err := os.Open(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer func() { _ = f.Close() }()
|
||||||
|
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var results []core.Suggestion
|
||||||
|
|
||||||
|
scanner := strings.NewReader("")
|
||||||
|
_ = scanner
|
||||||
|
|
||||||
|
data, _ := os.ReadFile(configPath)
|
||||||
|
for _, line := range strings.Split(string(data), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if !strings.HasPrefix(strings.ToLower(line), "host ") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
for _, host := range parts[1:] {
|
||||||
|
if strings.ContainsAny(host, "*?!") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if seen[host] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[host] = true
|
||||||
|
results = append(results, core.Suggestion{Cmd: host, Desc: "ssh host"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/versenilvis/iris/commands/core"
|
"github.com/versenilvis/iris/commands/core"
|
||||||
_ "github.com/versenilvis/iris/commands/fs" // Register z command
|
_ "github.com/versenilvis/iris/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIntegration_ZoxideMultiWord(t *testing.T) {
|
func TestIntegration_ZoxideMultiWord(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user