add repl that tunnels pm and shell commands
This commit is contained in:
@@ -1,17 +1,19 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/LazyBachelor/LazyPM/pkg"
|
"github.com/LazyBachelor/LazyPM/pkg"
|
||||||
"github.com/LazyBachelor/LazyPM/pkg/cli"
|
"github.com/LazyBachelor/LazyPM/pkg/cli"
|
||||||
"github.com/LazyBachelor/LazyPM/pkg/tui"
|
"github.com/LazyBachelor/LazyPM/pkg/tui"
|
||||||
"github.com/LazyBachelor/LazyPM/pkg/web"
|
"github.com/LazyBachelor/LazyPM/pkg/web"
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config := pkg.SurveyConfig{
|
config := pkg.SurveyConfig{
|
||||||
|
RootCmd: "pm",
|
||||||
WebAddress: "localhost:8080",
|
WebAddress: "localhost:8080",
|
||||||
IssuePrefix: "pm",
|
IssuePrefix: "pm",
|
||||||
BeadsDBPath: "./.pm/db.db",
|
BeadsDBPath: "./.pm/db.db",
|
||||||
@@ -25,7 +27,7 @@ func main() {
|
|||||||
case "tui":
|
case "tui":
|
||||||
err = tui.Run(ctx, config)
|
err = tui.Run(ctx, config)
|
||||||
case "cli":
|
case "cli":
|
||||||
err = cli.Run(ctx, config)
|
err = cli.RunREPL(ctx, config)
|
||||||
case "web":
|
case "web":
|
||||||
err = web.Run(ctx, config)
|
err = web.Run(ctx, config)
|
||||||
default:
|
default:
|
||||||
|
|||||||
9
go.mod
9
go.mod
@@ -6,11 +6,15 @@ require (
|
|||||||
github.com/Dicklesworthstone/beads_viewer v0.14.3
|
github.com/Dicklesworthstone/beads_viewer v0.14.3
|
||||||
github.com/NYTimes/gziphandler v1.1.1
|
github.com/NYTimes/gziphandler v1.1.1
|
||||||
github.com/a-h/templ v0.3.977
|
github.com/a-h/templ v0.3.977
|
||||||
|
github.com/c-bata/go-prompt v0.2.6
|
||||||
github.com/charmbracelet/bubbletea v1.3.10
|
github.com/charmbracelet/bubbletea v1.3.10
|
||||||
|
github.com/charmbracelet/huh v0.8.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/muesli/reflow v0.3.0
|
||||||
github.com/rs/cors v1.11.1
|
github.com/rs/cors v1.11.1
|
||||||
github.com/spf13/cobra v1.10.2
|
github.com/spf13/cobra v1.10.2
|
||||||
github.com/steveyegge/beads v0.49.3
|
github.com/steveyegge/beads v0.49.3
|
||||||
|
golang.org/x/term v0.39.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -27,7 +31,6 @@ require (
|
|||||||
github.com/charmbracelet/bubbles v0.21.1 // indirect
|
github.com/charmbracelet/bubbles v0.21.1 // indirect
|
||||||
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
||||||
github.com/charmbracelet/glamour v0.10.0 // indirect
|
github.com/charmbracelet/glamour v0.10.0 // indirect
|
||||||
github.com/charmbracelet/huh v0.8.0 // indirect
|
|
||||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect
|
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.11.5 // indirect
|
github.com/charmbracelet/x/ansi v0.11.5 // indirect
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
||||||
@@ -54,17 +57,18 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||||
|
github.com/mattn/go-tty v0.0.3 // indirect
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
|
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/muesli/reflow v0.3.0 // indirect
|
|
||||||
github.com/muesli/termenv v0.16.0 // indirect
|
github.com/muesli/termenv v0.16.0 // indirect
|
||||||
github.com/natefinch/atomic v1.0.1 // indirect
|
github.com/natefinch/atomic v1.0.1 // indirect
|
||||||
github.com/ncruces/go-sqlite3 v0.30.5 // indirect
|
github.com/ncruces/go-sqlite3 v0.30.5 // indirect
|
||||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||||
github.com/ncruces/julianday v1.0.0 // indirect
|
github.com/ncruces/julianday v1.0.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/pkg/term v1.2.0-beta.2 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||||
@@ -85,7 +89,6 @@ require (
|
|||||||
golang.org/x/net v0.49.0 // indirect
|
golang.org/x/net v0.49.0 // indirect
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.40.0 // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
golang.org/x/term v0.39.0 // indirect
|
|
||||||
golang.org/x/text v0.33.0 // indirect
|
golang.org/x/text v0.33.0 // indirect
|
||||||
golang.org/x/tools v0.41.0 // indirect
|
golang.org/x/tools v0.41.0 // indirect
|
||||||
gonum.org/v1/gonum v0.17.0 // indirect
|
gonum.org/v1/gonum v0.17.0 // indirect
|
||||||
|
|||||||
21
go.sum
21
go.sum
@@ -33,6 +33,8 @@ github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3v
|
|||||||
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
|
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
|
||||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
|
github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI=
|
||||||
|
github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY=
|
||||||
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
|
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
|
||||||
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
|
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
@@ -127,15 +129,24 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
|||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||||
|
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
|
github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI=
|
||||||
|
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||||
@@ -158,6 +169,8 @@ github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt
|
|||||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw=
|
||||||
|
github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
@@ -223,11 +236,19 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getIssueCmd = &cobra.Command{
|
var getIssueCmd = &cobra.Command{
|
||||||
Use: "describe [issue ID]",
|
Use: "describe [issue ID]",
|
||||||
Aliases: []string{"get", "read"},
|
|
||||||
Short: "Get issue details",
|
Short: "Get issue details",
|
||||||
Long: `Get issue details by ID`,
|
Long: `Get issue details by ID`,
|
||||||
RunE: runGetCmd,
|
Aliases: []string{"get", "read"},
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
|
RunE: runGetCmd,
|
||||||
ValidArgsFunction: completeIssues,
|
ValidArgsFunction: completeIssues,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,22 +45,30 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func completeIssues(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
func completeIssues(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
issues, _ := GetIssueCompletions(cmd.Context(), toComplete)
|
||||||
|
var ids []string
|
||||||
|
for _, issue := range issues {
|
||||||
|
ids = append(ids, issue.ID)
|
||||||
|
}
|
||||||
|
return ids, cobra.ShellCompDirectiveNoFileComp
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetIssueCompletions(ctx context.Context, toComplete string) ([]models.Issue, cobra.ShellCompDirective) {
|
||||||
if svc == nil {
|
if svc == nil {
|
||||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||||
}
|
}
|
||||||
|
|
||||||
issues, err := svc.Beads.AllIssues(cmd.Context())
|
issues, err := svc.Beads.AllIssues(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||||
}
|
}
|
||||||
|
|
||||||
var completions []string
|
var completions []models.Issue
|
||||||
for _, issue := range issues {
|
for _, issue := range issues {
|
||||||
|
|
||||||
if strings.HasPrefix(issue.ID, toComplete) {
|
if strings.HasPrefix(issue.ID, toComplete) {
|
||||||
completions = append(completions, issue.ID)
|
completions = append(completions, issue)
|
||||||
} else if strings.HasPrefix(issue.Title, toComplete) {
|
} else if strings.HasPrefix(issue.Title, toComplete) {
|
||||||
completions = append(completions, issue.ID)
|
completions = append(completions, issue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,17 @@ var rootCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Execute(services *service.Services) error {
|
func Execute(services *service.Services) error {
|
||||||
|
SetServices(services)
|
||||||
|
return rootCmd.Execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetServices(services *service.Services) {
|
||||||
svc = services
|
svc = services
|
||||||
rootCmd.Use = svc.Config.RootCmd
|
rootCmd.Use = svc.Config.RootCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteWithArgs(args []string) error {
|
||||||
|
rootCmd.SetArgs(args)
|
||||||
return rootCmd.Execute()
|
return rootCmd.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
243
pkg/cli/repl.go
Normal file
243
pkg/cli/repl.go
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||||
|
"github.com/LazyBachelor/LazyPM/pkg"
|
||||||
|
"github.com/LazyBachelor/LazyPM/pkg/cli/commands"
|
||||||
|
"github.com/c-bata/go-prompt"
|
||||||
|
"github.com/muesli/reflow/truncate"
|
||||||
|
"golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunREPL(ctx context.Context, config pkg.SurveyConfig) error {
|
||||||
|
oldState, err := term.GetState(int(os.Stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get terminal state: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
svc, cleanup, err := service.NewServices(ctx, config)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize services: %w", err)
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
commands.SetServices(svc)
|
||||||
|
|
||||||
|
fmt.Printf("Welcome to Project Management CLI! Type 'pm help' for available commands.\n")
|
||||||
|
fmt.Printf("You can also run shell commands directly. Type 'exit' or 'quit' to leave.\n")
|
||||||
|
|
||||||
|
for {
|
||||||
|
input := prompt.Input(
|
||||||
|
"› ",
|
||||||
|
completer,
|
||||||
|
prompt.OptionMaxSuggestion(5),
|
||||||
|
prompt.OptionSuggestionBGColor(prompt.DefaultColor),
|
||||||
|
prompt.OptionSelectedSuggestionBGColor(prompt.DefaultColor),
|
||||||
|
prompt.OptionDescriptionBGColor(prompt.DefaultColor),
|
||||||
|
prompt.OptionSelectedDescriptionBGColor(prompt.DefaultColor),
|
||||||
|
prompt.OptionPreviewSuggestionBGColor(prompt.DefaultColor),
|
||||||
|
prompt.OptionScrollbarBGColor(prompt.DefaultColor),
|
||||||
|
)
|
||||||
|
|
||||||
|
input = strings.TrimSpace(input)
|
||||||
|
|
||||||
|
if input == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if input == "exit" || input == "quit" {
|
||||||
|
fmt.Println("Goodbye!")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's a PM command (starts with "pm ")
|
||||||
|
if after, ok := strings.CutPrefix(input, "pm "); ok {
|
||||||
|
// Strip "pm " prefix and execute as PM command
|
||||||
|
pmCmd := after
|
||||||
|
pmCmd = strings.TrimSpace(pmCmd)
|
||||||
|
|
||||||
|
if pmCmd == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
args := strings.Fields(pmCmd)
|
||||||
|
if err := commands.ExecuteWithArgs(args); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Execute as shell command
|
||||||
|
cmd := exec.Command("sh", "-c", input)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore terminal state
|
||||||
|
if err := term.Restore(int(os.Stdin.Fd()), oldState); err != nil {
|
||||||
|
return fmt.Errorf("failed to restore terminal state: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func completer(d prompt.Document) []prompt.Suggest {
|
||||||
|
text := d.TextBeforeCursor()
|
||||||
|
words := strings.Fields(text)
|
||||||
|
|
||||||
|
// Only provide PM command completions if input starts with "pm"
|
||||||
|
if len(words) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If first word is "pm", provide PM command completions
|
||||||
|
if words[0] == "pm" {
|
||||||
|
// Remove "pm" from words to get the actual command
|
||||||
|
if len(words) == 1 || (len(words) == 2 && !strings.HasSuffix(text, " ")) {
|
||||||
|
return commandSuggestions(words[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the PM subcommand
|
||||||
|
if len(words) >= 2 {
|
||||||
|
cmd := words[1]
|
||||||
|
return flagSuggestions(cmd, words[1:], text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func commandSuggestions(words []string) []prompt.Suggest {
|
||||||
|
suggestions := []prompt.Suggest{
|
||||||
|
{Text: "help", Description: "Show help information"},
|
||||||
|
{Text: "create", Description: "Create a new issue"},
|
||||||
|
{Text: "describe", Description: "Get issue details by ID"},
|
||||||
|
{Text: "list", Description: "List all issues"},
|
||||||
|
{Text: "ls", Description: "List all issues"},
|
||||||
|
{Text: "search", Description: "Alias for ls"},
|
||||||
|
{Text: "get", Description: "Alias for describe"},
|
||||||
|
{Text: "read", Description: "Alias for describe"},
|
||||||
|
{Text: "add", Description: "Alias for create"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(words) == 0 {
|
||||||
|
return suggestions
|
||||||
|
}
|
||||||
|
|
||||||
|
var filtered []prompt.Suggest
|
||||||
|
for _, s := range suggestions {
|
||||||
|
if strings.HasPrefix(s.Text, words[0]) {
|
||||||
|
filtered = append(filtered, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagSuggestions(cmd string, words []string, text string) []prompt.Suggest {
|
||||||
|
var flagSuggests []prompt.Suggest
|
||||||
|
isCompleteWord := strings.HasSuffix(text, " ")
|
||||||
|
lastWord := ""
|
||||||
|
if len(words) > 0 && !isCompleteWord {
|
||||||
|
lastWord = words[len(words)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
switch cmd {
|
||||||
|
case "create", "add":
|
||||||
|
flagSuggests = []prompt.Suggest{
|
||||||
|
{Text: "--desc", Description: "Issue description"},
|
||||||
|
{Text: "--status", Description: "Issue status (open, closed, in_progress)"},
|
||||||
|
{Text: "--type", Description: "Issue type (bug, feature, task)"},
|
||||||
|
{Text: "--priority", Description: "Issue priority (0-5)"},
|
||||||
|
}
|
||||||
|
return filterAndCompleteFlags(flagSuggests, lastWord, words)
|
||||||
|
|
||||||
|
case "ls", "list", "search":
|
||||||
|
flagSuggests = []prompt.Suggest{
|
||||||
|
{Text: "--title", Description: "Filter by title"},
|
||||||
|
{Text: "--desc", Description: "Filter by description"},
|
||||||
|
{Text: "--status", Description: "Filter by status (open, closed, in_progress)"},
|
||||||
|
{Text: "--type", Description: "Filter by type (bug, feature, task)"},
|
||||||
|
{Text: "--priority", Description: "Filter by priority (0-5)"},
|
||||||
|
{Text: "--limit", Description: "Limit number of results"},
|
||||||
|
}
|
||||||
|
return filterAndCompleteFlags(flagSuggests, lastWord, words)
|
||||||
|
|
||||||
|
case "describe", "get", "read":
|
||||||
|
return issueIdSuggestions(words)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func issueIdSuggestions(words []string) []prompt.Suggest {
|
||||||
|
if len(words) < 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the partial ID being typed
|
||||||
|
partial := ""
|
||||||
|
if len(words) >= 2 {
|
||||||
|
partial = words[len(words)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
issues, _ := commands.GetIssueCompletions(context.Background(), partial)
|
||||||
|
|
||||||
|
var suggestions []prompt.Suggest
|
||||||
|
for _, issue := range issues {
|
||||||
|
suggestions = append(suggestions, prompt.Suggest{
|
||||||
|
Text: issue.ID, Description: truncate.String(issue.Title, 20),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return suggestions
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterAndCompleteFlags(suggestions []prompt.Suggest, lastWord string, words []string) []prompt.Suggest {
|
||||||
|
if len(words) >= 2 {
|
||||||
|
prevWord := words[len(words)-2]
|
||||||
|
switch prevWord {
|
||||||
|
case "-s", "--status":
|
||||||
|
return []prompt.Suggest{
|
||||||
|
{Text: "open", Description: "Open status"},
|
||||||
|
{Text: "closed", Description: "Closed status"},
|
||||||
|
{Text: "in_progress", Description: "In progress status"},
|
||||||
|
}
|
||||||
|
case "-t", "--type":
|
||||||
|
return []prompt.Suggest{
|
||||||
|
{Text: "bug", Description: "Bug issue type"},
|
||||||
|
{Text: "feature", Description: "Feature issue type"},
|
||||||
|
{Text: "task", Description: "Task issue type"},
|
||||||
|
}
|
||||||
|
case "-p", "--priority":
|
||||||
|
return []prompt.Suggest{
|
||||||
|
{Text: "0", Description: "Lowest priority"},
|
||||||
|
{Text: "1", Description: "Low priority"},
|
||||||
|
{Text: "2", Description: "Medium-low priority"},
|
||||||
|
{Text: "3", Description: "Medium priority"},
|
||||||
|
{Text: "4", Description: "High priority"},
|
||||||
|
{Text: "5", Description: "Highest priority"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastWord == "" {
|
||||||
|
return suggestions
|
||||||
|
}
|
||||||
|
|
||||||
|
var filtered []prompt.Suggest
|
||||||
|
for _, s := range suggestions {
|
||||||
|
if strings.HasPrefix(s.Text, lastWord) {
|
||||||
|
filtered = append(filtered, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user