Detailed changes
@@ -185,6 +185,19 @@ tasks:
- go get charm.land/catwalk
- go mod tidy
+ swag:
+ desc: Generate OpenAPI spec from swag annotations
+ cmds:
+ - go run github.com/swaggo/swag/cmd/swag@v1.16.6 init --generalInfo main.go --dir . --output docs --parseDependency --parseInternal --parseDepth 5
+ sources:
+ - internal/server/*.go
+ - internal/proto/*.go
+ - main.go
+ generates:
+ - docs/docs.go
+ - docs/swagger.json
+ - docs/swagger.yaml
+
sqlc:
desc: Generate code using SQLC
cmds:
@@ -14,6 +14,7 @@ require (
charm.land/x/vcr v0.1.1
github.com/JohannesKaufmann/html-to-markdown v1.6.0
github.com/MakeNowJust/heredoc v1.0.0
+ github.com/Microsoft/go-winio v0.6.2
github.com/PuerkitoBio/goquery v1.12.0
github.com/alecthomas/chroma/v2 v2.23.1
github.com/atotto/clipboard v0.1.4
@@ -59,12 +60,15 @@ require (
github.com/sourcegraph/jsonrpc2 v0.2.1
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
+ github.com/swaggo/http-swagger/v2 v2.0.2
+ github.com/swaggo/swag v1.16.6
github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
github.com/zeebo/xxh3 v1.1.0
go.uber.org/goleak v1.3.0
golang.org/x/net v0.52.0
golang.org/x/sync v0.20.0
+ golang.org/x/sys v0.42.0
golang.org/x/text v0.35.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
@@ -81,7 +85,7 @@ require (
git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
- github.com/Microsoft/go-winio v0.6.2 // indirect
+ github.com/KyleBanks/depth v1.2.1 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.3 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 // indirect
@@ -119,6 +123,10 @@ require (
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
+ github.com/go-openapi/jsonpointer v0.19.5 // indirect
+ github.com/go-openapi/jsonreference v0.20.0 // indirect
+ github.com/go-openapi/spec v0.20.6 // indirect
+ github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
@@ -135,6 +143,7 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackmordaunt/icns/v3 v3.0.1 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
github.com/kaptinlin/go-i18n v0.2.12 // indirect
github.com/kaptinlin/jsonpointer v0.4.17 // indirect
github.com/kaptinlin/jsonschema v0.7.5 // indirect
@@ -164,6 +173,7 @@ require (
github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect
github.com/spf13/pflag v1.0.9 // indirect
+ github.com/swaggo/files/v2 v2.0.0 // indirect
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
github.com/tetratelabs/wazero v1.11.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
@@ -186,10 +196,11 @@ require (
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
golang.org/x/image v0.36.0 // indirect
+ golang.org/x/mod v0.33.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
- golang.org/x/sys v0.42.0 // indirect
golang.org/x/term v0.41.0 // indirect
golang.org/x/time v0.14.0 // indirect
+ golang.org/x/tools v0.42.0 // indirect
google.golang.org/api v0.269.0 // indirect
google.golang.org/genai v1.49.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
@@ -198,6 +209,7 @@ require (
gopkg.in/dnaeon/go-vcr.v4 v4.0.6-0.20251110073552-01de4eb40290 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
modernc.org/libc v1.68.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
@@ -36,6 +36,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mx
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/JohannesKaufmann/html-to-markdown v1.6.0 h1:04VXMiE50YYfCfLboJCLcgqF5x+rHJnb1ssNmqpLH/k=
github.com/JohannesKaufmann/html-to-markdown v1.6.0/go.mod h1:NUI78lGg/a7vpEJTz/0uOcYMaibytE4BUOQS8k78yPQ=
+github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
+github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
@@ -139,6 +141,7 @@ github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJ
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -188,6 +191,16 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
+github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
+github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ=
+github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
+github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
@@ -241,6 +254,7 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jordanella/go-ansi-paintbrush v0.0.0-20240728195301-b7ad996ecf3d h1:on25kP+Sx7sxUMRQiA8gdcToAGet4DK/EIA30mXre+4=
github.com/jordanella/go-ansi-paintbrush v0.0.0-20240728195301-b7ad996ecf3d/go.mod h1:SV0W0APWP9MZ1/gfDQ/NzzTlWdIgYZ/ZbpN4d/UXRYw=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kaptinlin/go-i18n v0.2.12 h1:ywDsvb4KDFddMC2dpI/rrIzGU2mWUSvHmWUm9BMsdl4=
github.com/kaptinlin/go-i18n v0.2.12/go.mod h1:pVcu9qsW5pOIOoZFJXesRYmLos1vMQrby70JPAoWmJU=
@@ -267,6 +281,9 @@ 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/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/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -299,6 +316,7 @@ github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
github.com/openai/openai-go/v3 v3.28.0 h1:2+FfrCVMdGXSQrBv1tLWtokm+BU7+3hJ/8rAHPQ63KM=
@@ -359,11 +377,18 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
+github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
+github.com/swaggo/http-swagger/v2 v2.0.2 h1:FKCdLsl+sFCx60KFsyM0rDarwiUSZ8DqbfSyIKC9OBg=
+github.com/swaggo/http-swagger/v2 v2.0.2/go.mod h1:r7/GBkAWIfK6E/OLnE8fXnviHiDeAHmgIyooa4xm3AQ=
+github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
+github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk=
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o=
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
@@ -532,7 +557,9 @@ google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhH
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/dnaeon/go-vcr.v4 v4.0.6-0.20251110073552-01de4eb40290 h1:g3ah7zaWmw41EtOgBNXpx8zk4HYuH3OMwB+qh1Dt834=
@@ -547,6 +574,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
@@ -0,0 +1,92 @@
+package proto
+
+import "github.com/charmbracelet/crush/internal/config"
+
+// ConfigSetRequest represents a request to set a config field.
+type ConfigSetRequest struct {
+ Scope config.Scope `json:"scope"`
+ Key string `json:"key"`
+ Value any `json:"value"`
+}
+
+// ConfigRemoveRequest represents a request to remove a config field.
+type ConfigRemoveRequest struct {
+ Scope config.Scope `json:"scope"`
+ Key string `json:"key"`
+}
+
+// ConfigModelRequest represents a request to update the preferred model.
+type ConfigModelRequest struct {
+ Scope config.Scope `json:"scope"`
+ ModelType config.SelectedModelType `json:"model_type"`
+ Model config.SelectedModel `json:"model"`
+}
+
+// ConfigCompactRequest represents a request to set compact mode.
+type ConfigCompactRequest struct {
+ Scope config.Scope `json:"scope"`
+ Enabled bool `json:"enabled"`
+}
+
+// ConfigProviderKeyRequest represents a request to set a provider API key.
+type ConfigProviderKeyRequest struct {
+ Scope config.Scope `json:"scope"`
+ ProviderID string `json:"provider_id"`
+ APIKey any `json:"api_key"`
+}
+
+// ConfigRefreshOAuthRequest represents a request to refresh an OAuth token.
+type ConfigRefreshOAuthRequest struct {
+ Scope config.Scope `json:"scope"`
+ ProviderID string `json:"provider_id"`
+}
+
+// ImportCopilotResponse represents the response from importing Copilot credentials.
+type ImportCopilotResponse struct {
+ Token any `json:"token"`
+ Success bool `json:"success"`
+}
+
+// ProjectNeedsInitResponse represents whether a project needs initialization.
+type ProjectNeedsInitResponse struct {
+ NeedsInit bool `json:"needs_init"`
+}
+
+// ProjectInitPromptResponse represents the project initialization prompt.
+type ProjectInitPromptResponse struct {
+ Prompt string `json:"prompt"`
+}
+
+// LSPStartRequest represents a request to start an LSP for a path.
+type LSPStartRequest struct {
+ Path string `json:"path"`
+}
+
+// FileTrackerReadRequest represents a request to record a file read.
+type FileTrackerReadRequest struct {
+ SessionID string `json:"session_id"`
+ Path string `json:"path"`
+}
+
+// MCPNameRequest represents a request targeting a named MCP server.
+type MCPNameRequest struct {
+ Name string `json:"name"`
+}
+
+// MCPReadResourceRequest represents a request to read an MCP resource.
+type MCPReadResourceRequest struct {
+ Name string `json:"name"`
+ URI string `json:"uri"`
+}
+
+// MCPGetPromptRequest represents a request to get an MCP prompt.
+type MCPGetPromptRequest struct {
+ ClientID string `json:"client_id"`
+ PromptID string `json:"prompt_id"`
+ Args map[string]string `json:"args"`
+}
+
+// MCPGetPromptResponse represents the response from getting an MCP prompt.
+type MCPGetPromptResponse struct {
+ Prompt string `json:"prompt"`
+}
@@ -4,18 +4,25 @@ import (
"encoding/json"
"net/http"
- "github.com/charmbracelet/crush/internal/config"
"github.com/charmbracelet/crush/internal/proto"
)
+// handlePostWorkspaceConfigSet sets a configuration field.
+//
+// @Summary Set a config field
+// @Tags config
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.ConfigSetRequest true "Config set request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/config/set [post]
func (c *controllerV1) handlePostWorkspaceConfigSet(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Scope config.Scope `json:"scope"`
- Key string `json:"key"`
- Value any `json:"value"`
- }
+ var req proto.ConfigSetRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -29,13 +36,22 @@ func (c *controllerV1) handlePostWorkspaceConfigSet(w http.ResponseWriter, r *ht
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceConfigRemove removes a configuration field.
+//
+// @Summary Remove a config field
+// @Tags config
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.ConfigRemoveRequest true "Config remove request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/config/remove [post]
func (c *controllerV1) handlePostWorkspaceConfigRemove(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Scope config.Scope `json:"scope"`
- Key string `json:"key"`
- }
+ var req proto.ConfigRemoveRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -49,14 +65,22 @@ func (c *controllerV1) handlePostWorkspaceConfigRemove(w http.ResponseWriter, r
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceConfigModel updates the preferred model.
+//
+// @Summary Set the preferred model
+// @Tags config
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.ConfigModelRequest true "Config model request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/config/model [post]
func (c *controllerV1) handlePostWorkspaceConfigModel(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Scope config.Scope `json:"scope"`
- ModelType config.SelectedModelType `json:"model_type"`
- Model config.SelectedModel `json:"model"`
- }
+ var req proto.ConfigModelRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -70,13 +94,22 @@ func (c *controllerV1) handlePostWorkspaceConfigModel(w http.ResponseWriter, r *
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceConfigCompact sets compact mode.
+//
+// @Summary Set compact mode
+// @Tags config
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.ConfigCompactRequest true "Config compact request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/config/compact [post]
func (c *controllerV1) handlePostWorkspaceConfigCompact(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Scope config.Scope `json:"scope"`
- Enabled bool `json:"enabled"`
- }
+ var req proto.ConfigCompactRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -90,14 +123,22 @@ func (c *controllerV1) handlePostWorkspaceConfigCompact(w http.ResponseWriter, r
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceConfigProviderKey sets a provider API key.
+//
+// @Summary Set provider API key
+// @Tags config
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.ConfigProviderKeyRequest true "Config provider key request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/config/provider-key [post]
func (c *controllerV1) handlePostWorkspaceConfigProviderKey(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Scope config.Scope `json:"scope"`
- ProviderID string `json:"provider_id"`
- APIKey any `json:"api_key"`
- }
+ var req proto.ConfigProviderKeyRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -111,6 +152,16 @@ func (c *controllerV1) handlePostWorkspaceConfigProviderKey(w http.ResponseWrite
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceConfigImportCopilot imports Copilot credentials.
+//
+// @Summary Import Copilot credentials
+// @Tags config
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {object} proto.ImportCopilotResponse
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/config/import-copilot [post]
func (c *controllerV1) handlePostWorkspaceConfigImportCopilot(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
token, ok, err := c.backend.ImportCopilot(id)
@@ -118,19 +169,25 @@ func (c *controllerV1) handlePostWorkspaceConfigImportCopilot(w http.ResponseWri
c.handleError(w, r, err)
return
}
- jsonEncode(w, struct {
- Token any `json:"token"`
- Success bool `json:"success"`
- }{Token: token, Success: ok})
+ jsonEncode(w, proto.ImportCopilotResponse{Token: token, Success: ok})
}
+// handlePostWorkspaceConfigRefreshOAuth refreshes an OAuth token for a provider.
+//
+// @Summary Refresh OAuth token
+// @Tags config
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.ConfigRefreshOAuthRequest true "Refresh OAuth request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/config/refresh-oauth [post]
func (c *controllerV1) handlePostWorkspaceConfigRefreshOAuth(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Scope config.Scope `json:"scope"`
- ProviderID string `json:"provider_id"`
- }
+ var req proto.ConfigRefreshOAuthRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -144,6 +201,16 @@ func (c *controllerV1) handlePostWorkspaceConfigRefreshOAuth(w http.ResponseWrit
w.WriteHeader(http.StatusOK)
}
+// handleGetWorkspaceProjectNeedsInit reports whether a project needs initialization.
+//
+// @Summary Check if project needs initialization
+// @Tags project
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {object} proto.ProjectNeedsInitResponse
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/project/needs-init [get]
func (c *controllerV1) handleGetWorkspaceProjectNeedsInit(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
needs, err := c.backend.ProjectNeedsInitialization(id)
@@ -151,11 +218,18 @@ func (c *controllerV1) handleGetWorkspaceProjectNeedsInit(w http.ResponseWriter,
c.handleError(w, r, err)
return
}
- jsonEncode(w, struct {
- NeedsInit bool `json:"needs_init"`
- }{NeedsInit: needs})
+ jsonEncode(w, proto.ProjectNeedsInitResponse{NeedsInit: needs})
}
+// handlePostWorkspaceProjectInit marks the project as initialized.
+//
+// @Summary Mark project as initialized
+// @Tags project
+// @Param id path string true "Workspace ID"
+// @Success 200
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/project/init [post]
func (c *controllerV1) handlePostWorkspaceProjectInit(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if err := c.backend.MarkProjectInitialized(id); err != nil {
@@ -165,6 +239,16 @@ func (c *controllerV1) handlePostWorkspaceProjectInit(w http.ResponseWriter, r *
w.WriteHeader(http.StatusOK)
}
+// handleGetWorkspaceProjectInitPrompt returns the project initialization prompt.
+//
+// @Summary Get project initialization prompt
+// @Tags project
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {object} proto.ProjectInitPromptResponse
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/project/init-prompt [get]
func (c *controllerV1) handleGetWorkspaceProjectInitPrompt(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
prompt, err := c.backend.InitializePrompt(id)
@@ -172,17 +256,25 @@ func (c *controllerV1) handleGetWorkspaceProjectInitPrompt(w http.ResponseWriter
c.handleError(w, r, err)
return
}
- jsonEncode(w, struct {
- Prompt string `json:"prompt"`
- }{Prompt: prompt})
+ jsonEncode(w, proto.ProjectInitPromptResponse{Prompt: prompt})
}
+// handlePostWorkspaceMCPRefreshTools refreshes tools for a named MCP server.
+//
+// @Summary Refresh MCP tools
+// @Tags mcp
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.MCPNameRequest true "MCP name request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/mcp/refresh-tools [post]
func (c *controllerV1) handlePostWorkspaceMCPRefreshTools(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Name string `json:"name"`
- }
+ var req proto.MCPNameRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -196,13 +288,23 @@ func (c *controllerV1) handlePostWorkspaceMCPRefreshTools(w http.ResponseWriter,
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceMCPReadResource reads a resource from an MCP server.
+//
+// @Summary Read MCP resource
+// @Tags mcp
+// @Accept json
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.MCPReadResourceRequest true "MCP read resource request"
+// @Success 200 {object} object
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/mcp/read-resource [post]
func (c *controllerV1) handlePostWorkspaceMCPReadResource(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Name string `json:"name"`
- URI string `json:"uri"`
- }
+ var req proto.MCPReadResourceRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -217,14 +319,23 @@ func (c *controllerV1) handlePostWorkspaceMCPReadResource(w http.ResponseWriter,
jsonEncode(w, contents)
}
+// handlePostWorkspaceMCPGetPrompt retrieves a prompt from an MCP server.
+//
+// @Summary Get MCP prompt
+// @Tags mcp
+// @Accept json
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.MCPGetPromptRequest true "MCP get prompt request"
+// @Success 200 {object} proto.MCPGetPromptResponse
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/mcp/get-prompt [post]
func (c *controllerV1) handlePostWorkspaceMCPGetPrompt(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- ClientID string `json:"client_id"`
- PromptID string `json:"prompt_id"`
- Args map[string]string `json:"args"`
- }
+ var req proto.MCPGetPromptRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -236,11 +347,19 @@ func (c *controllerV1) handlePostWorkspaceMCPGetPrompt(w http.ResponseWriter, r
c.handleError(w, r, err)
return
}
- jsonEncode(w, struct {
- Prompt string `json:"prompt"`
- }{Prompt: prompt})
+ jsonEncode(w, proto.MCPGetPromptResponse{Prompt: prompt})
}
+// handleGetWorkspaceMCPStates returns the state of all MCP clients.
+//
+// @Summary Get MCP client states
+// @Tags mcp
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {object} map[string]proto.MCPClientInfo
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/mcp/states [get]
func (c *controllerV1) handleGetWorkspaceMCPStates(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
states := c.backend.MCPGetStates(id)
@@ -259,12 +378,22 @@ func (c *controllerV1) handleGetWorkspaceMCPStates(w http.ResponseWriter, r *htt
jsonEncode(w, result)
}
+// handlePostWorkspaceMCPRefreshPrompts refreshes prompts for a named MCP server.
+//
+// @Summary Refresh MCP prompts
+// @Tags mcp
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.MCPNameRequest true "MCP name request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/mcp/refresh-prompts [post]
func (c *controllerV1) handlePostWorkspaceMCPRefreshPrompts(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Name string `json:"name"`
- }
+ var req proto.MCPNameRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -275,12 +404,22 @@ func (c *controllerV1) handlePostWorkspaceMCPRefreshPrompts(w http.ResponseWrite
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceMCPRefreshResources refreshes resources for a named MCP server.
+//
+// @Summary Refresh MCP resources
+// @Tags mcp
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.MCPNameRequest true "MCP name request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/mcp/refresh-resources [post]
func (c *controllerV1) handlePostWorkspaceMCPRefreshResources(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Name string `json:"name"`
- }
+ var req proto.MCPNameRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -16,14 +16,36 @@ type controllerV1 struct {
server *Server
}
+// handleGetHealth checks server health.
+//
+// @Summary Health check
+// @Tags system
+// @Success 200
+// @Router /health [get]
func (c *controllerV1) handleGetHealth(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}
+// handleGetVersion returns server version information.
+//
+// @Summary Get server version
+// @Tags system
+// @Produce json
+// @Success 200 {object} proto.VersionInfo
+// @Router /version [get]
func (c *controllerV1) handleGetVersion(w http.ResponseWriter, _ *http.Request) {
jsonEncode(w, c.backend.VersionInfo())
}
+// handlePostControl sends a control command to the server.
+//
+// @Summary Send server control command
+// @Tags system
+// @Accept json
+// @Param request body proto.ServerControl true "Control command (e.g. shutdown)"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Router /control [post]
func (c *controllerV1) handlePostControl(w http.ResponseWriter, r *http.Request) {
var req proto.ServerControl
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -42,14 +64,38 @@ func (c *controllerV1) handlePostControl(w http.ResponseWriter, r *http.Request)
}
}
+// handleGetConfig returns global server configuration.
+//
+// @Summary Get server config
+// @Tags system
+// @Produce json
+// @Success 200 {object} object
+// @Router /config [get]
func (c *controllerV1) handleGetConfig(w http.ResponseWriter, _ *http.Request) {
jsonEncode(w, c.backend.Config())
}
+// handleGetWorkspaces lists all workspaces.
+//
+// @Summary List workspaces
+// @Tags workspaces
+// @Produce json
+// @Success 200 {array} proto.Workspace
+// @Router /workspaces [get]
func (c *controllerV1) handleGetWorkspaces(w http.ResponseWriter, _ *http.Request) {
jsonEncode(w, c.backend.ListWorkspaces())
}
+// handleGetWorkspace returns a single workspace by ID.
+//
+// @Summary Get workspace
+// @Tags workspaces
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {object} proto.Workspace
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id} [get]
func (c *controllerV1) handleGetWorkspace(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
ws, err := c.backend.GetWorkspaceProto(id)
@@ -60,6 +106,17 @@ func (c *controllerV1) handleGetWorkspace(w http.ResponseWriter, r *http.Request
jsonEncode(w, ws)
}
+// handlePostWorkspaces creates a new workspace.
+//
+// @Summary Create workspace
+// @Tags workspaces
+// @Accept json
+// @Produce json
+// @Param request body proto.Workspace true "Workspace creation params"
+// @Success 200 {object} proto.Workspace
+// @Failure 400 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces [post]
func (c *controllerV1) handlePostWorkspaces(w http.ResponseWriter, r *http.Request) {
var args proto.Workspace
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
@@ -76,11 +133,29 @@ func (c *controllerV1) handlePostWorkspaces(w http.ResponseWriter, r *http.Reque
jsonEncode(w, result)
}
+// handleDeleteWorkspaces deletes a workspace.
+//
+// @Summary Delete workspace
+// @Tags workspaces
+// @Param id path string true "Workspace ID"
+// @Success 200
+// @Failure 404 {object} proto.Error
+// @Router /workspaces/{id} [delete]
func (c *controllerV1) handleDeleteWorkspaces(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
c.backend.DeleteWorkspace(id)
}
+// handleGetWorkspaceConfig returns workspace configuration.
+//
+// @Summary Get workspace config
+// @Tags workspaces
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {object} object
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/config [get]
func (c *controllerV1) handleGetWorkspaceConfig(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
cfg, err := c.backend.GetWorkspaceConfig(id)
@@ -91,6 +166,16 @@ func (c *controllerV1) handleGetWorkspaceConfig(w http.ResponseWriter, r *http.R
jsonEncode(w, cfg)
}
+// handleGetWorkspaceProviders lists available providers for a workspace.
+//
+// @Summary Get workspace providers
+// @Tags workspaces
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {object} object
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/providers [get]
func (c *controllerV1) handleGetWorkspaceProviders(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
providers, err := c.backend.GetWorkspaceProviders(id)
@@ -101,6 +186,16 @@ func (c *controllerV1) handleGetWorkspaceProviders(w http.ResponseWriter, r *htt
jsonEncode(w, providers)
}
+// handleGetWorkspaceEvents streams workspace events as Server-Sent Events.
+//
+// @Summary Stream workspace events (SSE)
+// @Tags workspaces
+// @Produce text/event-stream
+// @Param id path string true "Workspace ID"
+// @Success 200
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/events [get]
func (c *controllerV1) handleGetWorkspaceEvents(w http.ResponseWriter, r *http.Request) {
flusher := http.NewResponseController(w)
id := r.PathValue("id")
@@ -140,6 +235,16 @@ func (c *controllerV1) handleGetWorkspaceEvents(w http.ResponseWriter, r *http.R
}
}
+// handleGetWorkspaceLSPs lists LSP clients for a workspace.
+//
+// @Summary List LSP clients
+// @Tags lsp
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {object} map[string]proto.LSPClientInfo
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/lsps [get]
func (c *controllerV1) handleGetWorkspaceLSPs(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
states, err := c.backend.GetLSPStates(id)
@@ -160,6 +265,17 @@ func (c *controllerV1) handleGetWorkspaceLSPs(w http.ResponseWriter, r *http.Req
jsonEncode(w, result)
}
+// handleGetWorkspaceLSPDiagnostics returns diagnostics for an LSP client.
+//
+// @Summary Get LSP diagnostics
+// @Tags lsp
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param lsp path string true "LSP client name"
+// @Success 200 {object} object
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/lsps/{lsp}/diagnostics [get]
func (c *controllerV1) handleGetWorkspaceLSPDiagnostics(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
lspName := r.PathValue("lsp")
@@ -171,6 +287,16 @@ func (c *controllerV1) handleGetWorkspaceLSPDiagnostics(w http.ResponseWriter, r
jsonEncode(w, diagnostics)
}
+// handleGetWorkspaceSessions lists sessions for a workspace.
+//
+// @Summary List sessions
+// @Tags sessions
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {array} proto.Session
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/sessions [get]
func (c *controllerV1) handleGetWorkspaceSessions(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sessions, err := c.backend.ListSessions(r.Context(), id)
@@ -181,6 +307,19 @@ func (c *controllerV1) handleGetWorkspaceSessions(w http.ResponseWriter, r *http
jsonEncode(w, sessions)
}
+// handlePostWorkspaceSessions creates a new session in a workspace.
+//
+// @Summary Create session
+// @Tags sessions
+// @Accept json
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.Session true "Session creation params (title)"
+// @Success 200 {object} proto.Session
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/sessions [post]
func (c *controllerV1) handlePostWorkspaceSessions(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
@@ -199,6 +338,17 @@ func (c *controllerV1) handlePostWorkspaceSessions(w http.ResponseWriter, r *htt
jsonEncode(w, sess)
}
+// handleGetWorkspaceSession returns a single session.
+//
+// @Summary Get session
+// @Tags sessions
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200 {object} proto.Session
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/sessions/{sid} [get]
func (c *controllerV1) handleGetWorkspaceSession(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -210,6 +360,17 @@ func (c *controllerV1) handleGetWorkspaceSession(w http.ResponseWriter, r *http.
jsonEncode(w, sess)
}
+// handleGetWorkspaceSessionHistory returns the history for a session.
+//
+// @Summary Get session history
+// @Tags sessions
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200 {array} proto.File
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/sessions/{sid}/history [get]
func (c *controllerV1) handleGetWorkspaceSessionHistory(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -221,6 +382,17 @@ func (c *controllerV1) handleGetWorkspaceSessionHistory(w http.ResponseWriter, r
jsonEncode(w, history)
}
+// handleGetWorkspaceSessionMessages returns all messages for a session.
+//
+// @Summary Get session messages
+// @Tags sessions
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200 {array} proto.Message
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/sessions/{sid}/messages [get]
func (c *controllerV1) handleGetWorkspaceSessionMessages(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -232,6 +404,20 @@ func (c *controllerV1) handleGetWorkspaceSessionMessages(w http.ResponseWriter,
jsonEncode(w, messagesToProto(messages))
}
+// handlePutWorkspaceSession updates a session.
+//
+// @Summary Update session
+// @Tags sessions
+// @Accept json
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Param request body proto.Session true "Updated session"
+// @Success 200 {object} proto.Session
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/sessions/{sid} [put]
func (c *controllerV1) handlePutWorkspaceSession(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
@@ -250,6 +436,16 @@ func (c *controllerV1) handlePutWorkspaceSession(w http.ResponseWriter, r *http.
jsonEncode(w, saved)
}
+// handleDeleteWorkspaceSession deletes a session.
+//
+// @Summary Delete session
+// @Tags sessions
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/sessions/{sid} [delete]
func (c *controllerV1) handleDeleteWorkspaceSession(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -260,6 +456,17 @@ func (c *controllerV1) handleDeleteWorkspaceSession(w http.ResponseWriter, r *ht
w.WriteHeader(http.StatusOK)
}
+// handleGetWorkspaceSessionUserMessages returns user messages for a session.
+//
+// @Summary Get user messages for session
+// @Tags sessions
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200 {array} proto.Message
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/sessions/{sid}/messages/user [get]
func (c *controllerV1) handleGetWorkspaceSessionUserMessages(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -271,6 +478,16 @@ func (c *controllerV1) handleGetWorkspaceSessionUserMessages(w http.ResponseWrit
jsonEncode(w, messagesToProto(messages))
}
+// handleGetWorkspaceAllUserMessages returns all user messages across sessions.
+//
+// @Summary Get all user messages for workspace
+// @Tags workspaces
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {array} proto.Message
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/messages/user [get]
func (c *controllerV1) handleGetWorkspaceAllUserMessages(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
messages, err := c.backend.ListAllUserMessages(r.Context(), id)
@@ -281,6 +498,17 @@ func (c *controllerV1) handleGetWorkspaceAllUserMessages(w http.ResponseWriter,
jsonEncode(w, messagesToProto(messages))
}
+// handleGetWorkspaceSessionFileTrackerFiles lists files read in a session.
+//
+// @Summary List tracked files for session
+// @Tags filetracker
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200 {array} string
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/sessions/{sid}/filetracker/files [get]
func (c *controllerV1) handleGetWorkspaceSessionFileTrackerFiles(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -292,13 +520,22 @@ func (c *controllerV1) handleGetWorkspaceSessionFileTrackerFiles(w http.Response
jsonEncode(w, files)
}
+// handlePostWorkspaceFileTrackerRead records a file read event.
+//
+// @Summary Record file read
+// @Tags filetracker
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.FileTrackerReadRequest true "File tracker read request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/filetracker/read [post]
func (c *controllerV1) handlePostWorkspaceFileTrackerRead(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- SessionID string `json:"session_id"`
- Path string `json:"path"`
- }
+ var req proto.FileTrackerReadRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -312,6 +549,18 @@ func (c *controllerV1) handlePostWorkspaceFileTrackerRead(w http.ResponseWriter,
w.WriteHeader(http.StatusOK)
}
+// handleGetWorkspaceFileTrackerLastRead returns the last read time for a file.
+//
+// @Summary Get last read time for file
+// @Tags filetracker
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param session_id query string false "Session ID"
+// @Param path query string true "File path"
+// @Success 200 {object} object
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/filetracker/lastread [get]
func (c *controllerV1) handleGetWorkspaceFileTrackerLastRead(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.URL.Query().Get("session_id")
@@ -325,12 +574,22 @@ func (c *controllerV1) handleGetWorkspaceFileTrackerLastRead(w http.ResponseWrit
jsonEncode(w, t)
}
+// handlePostWorkspaceLSPStart starts an LSP server for a path.
+//
+// @Summary Start LSP server
+// @Tags lsp
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.LSPStartRequest true "LSP start request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/lsps/start [post]
func (c *controllerV1) handlePostWorkspaceLSPStart(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
- var req struct {
- Path string `json:"path"`
- }
+ var req proto.LSPStartRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
c.server.logError(r, "Failed to decode request", "error", err)
jsonError(w, http.StatusBadRequest, "failed to decode request")
@@ -344,6 +603,15 @@ func (c *controllerV1) handlePostWorkspaceLSPStart(w http.ResponseWriter, r *htt
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceLSPStopAll stops all LSP servers.
+//
+// @Summary Stop all LSP servers
+// @Tags lsp
+// @Param id path string true "Workspace ID"
+// @Success 200
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/lsps/stop [post]
func (c *controllerV1) handlePostWorkspaceLSPStopAll(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if err := c.backend.LSPStopAll(r.Context(), id); err != nil {
@@ -353,6 +621,16 @@ func (c *controllerV1) handlePostWorkspaceLSPStopAll(w http.ResponseWriter, r *h
w.WriteHeader(http.StatusOK)
}
+// handleGetWorkspaceAgent returns agent info for a workspace.
+//
+// @Summary Get agent info
+// @Tags agent
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {object} proto.AgentInfo
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent [get]
func (c *controllerV1) handleGetWorkspaceAgent(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
info, err := c.backend.GetAgentInfo(id)
@@ -363,6 +641,18 @@ func (c *controllerV1) handleGetWorkspaceAgent(w http.ResponseWriter, r *http.Re
jsonEncode(w, info)
}
+// handlePostWorkspaceAgent sends a message to the agent.
+//
+// @Summary Send message to agent
+// @Tags agent
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.AgentMessage true "Agent message"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent [post]
func (c *controllerV1) handlePostWorkspaceAgent(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
@@ -380,6 +670,15 @@ func (c *controllerV1) handlePostWorkspaceAgent(w http.ResponseWriter, r *http.R
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceAgentInit initializes the agent for a workspace.
+//
+// @Summary Initialize agent
+// @Tags agent
+// @Param id path string true "Workspace ID"
+// @Success 200
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent/init [post]
func (c *controllerV1) handlePostWorkspaceAgentInit(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if err := c.backend.InitAgent(r.Context(), id); err != nil {
@@ -389,6 +688,15 @@ func (c *controllerV1) handlePostWorkspaceAgentInit(w http.ResponseWriter, r *ht
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceAgentUpdate updates the agent for a workspace.
+//
+// @Summary Update agent
+// @Tags agent
+// @Param id path string true "Workspace ID"
+// @Success 200
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent/update [post]
func (c *controllerV1) handlePostWorkspaceAgentUpdate(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if err := c.backend.UpdateAgent(r.Context(), id); err != nil {
@@ -398,6 +706,17 @@ func (c *controllerV1) handlePostWorkspaceAgentUpdate(w http.ResponseWriter, r *
w.WriteHeader(http.StatusOK)
}
+// handleGetWorkspaceAgentSession returns a specific agent session.
+//
+// @Summary Get agent session
+// @Tags agent
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200 {object} proto.AgentSession
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent/sessions/{sid} [get]
func (c *controllerV1) handleGetWorkspaceAgentSession(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -409,6 +728,16 @@ func (c *controllerV1) handleGetWorkspaceAgentSession(w http.ResponseWriter, r *
jsonEncode(w, agentSession)
}
+// handlePostWorkspaceAgentSessionCancel cancels a running agent session.
+//
+// @Summary Cancel agent session
+// @Tags agent
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent/sessions/{sid}/cancel [post]
func (c *controllerV1) handlePostWorkspaceAgentSessionCancel(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -419,6 +748,17 @@ func (c *controllerV1) handlePostWorkspaceAgentSessionCancel(w http.ResponseWrit
w.WriteHeader(http.StatusOK)
}
+// handleGetWorkspaceAgentSessionPromptQueued returns whether a queued prompt exists.
+//
+// @Summary Get queued prompt status
+// @Tags agent
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200 {object} object
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent/sessions/{sid}/prompts/queued [get]
func (c *controllerV1) handleGetWorkspaceAgentSessionPromptQueued(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -430,6 +770,16 @@ func (c *controllerV1) handleGetWorkspaceAgentSessionPromptQueued(w http.Respons
jsonEncode(w, queued)
}
+// handlePostWorkspaceAgentSessionPromptClear clears the prompt queue for a session.
+//
+// @Summary Clear prompt queue
+// @Tags agent
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent/sessions/{sid}/prompts/clear [post]
func (c *controllerV1) handlePostWorkspaceAgentSessionPromptClear(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -440,6 +790,16 @@ func (c *controllerV1) handlePostWorkspaceAgentSessionPromptClear(w http.Respons
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspaceAgentSessionSummarize summarizes a session.
+//
+// @Summary Summarize session
+// @Tags agent
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent/sessions/{sid}/summarize [post]
func (c *controllerV1) handlePostWorkspaceAgentSessionSummarize(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -450,6 +810,17 @@ func (c *controllerV1) handlePostWorkspaceAgentSessionSummarize(w http.ResponseW
w.WriteHeader(http.StatusOK)
}
+// handleGetWorkspaceAgentSessionPromptList returns the list of queued prompts.
+//
+// @Summary List queued prompts
+// @Tags agent
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param sid path string true "Session ID"
+// @Success 200 {array} string
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent/sessions/{sid}/prompts/list [get]
func (c *controllerV1) handleGetWorkspaceAgentSessionPromptList(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
sid := r.PathValue("sid")
@@ -461,6 +832,17 @@ func (c *controllerV1) handleGetWorkspaceAgentSessionPromptList(w http.ResponseW
jsonEncode(w, prompts)
}
+// handleGetWorkspaceAgentDefaultSmallModel returns the default small model for a provider.
+//
+// @Summary Get default small model
+// @Tags agent
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Param provider_id query string false "Provider ID"
+// @Success 200 {object} object
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/agent/default-small-model [get]
func (c *controllerV1) handleGetWorkspaceAgentDefaultSmallModel(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
providerID := r.URL.Query().Get("provider_id")
@@ -472,6 +854,18 @@ func (c *controllerV1) handleGetWorkspaceAgentDefaultSmallModel(w http.ResponseW
jsonEncode(w, model)
}
+// handlePostWorkspacePermissionsGrant grants a permission request.
+//
+// @Summary Grant permission
+// @Tags permissions
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.PermissionGrant true "Permission grant"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/permissions/grant [post]
func (c *controllerV1) handlePostWorkspacePermissionsGrant(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
@@ -489,6 +883,18 @@ func (c *controllerV1) handlePostWorkspacePermissionsGrant(w http.ResponseWriter
w.WriteHeader(http.StatusOK)
}
+// handlePostWorkspacePermissionsSkip sets whether to skip permission prompts.
+//
+// @Summary Set skip permissions
+// @Tags permissions
+// @Accept json
+// @Param id path string true "Workspace ID"
+// @Param request body proto.PermissionSkipRequest true "Permission skip request"
+// @Success 200
+// @Failure 400 {object} proto.Error
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/permissions/skip [post]
func (c *controllerV1) handlePostWorkspacePermissionsSkip(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
@@ -505,6 +911,16 @@ func (c *controllerV1) handlePostWorkspacePermissionsSkip(w http.ResponseWriter,
}
}
+// handleGetWorkspacePermissionsSkip returns whether permission prompts are skipped.
+//
+// @Summary Get skip permissions status
+// @Tags permissions
+// @Produce json
+// @Param id path string true "Workspace ID"
+// @Success 200 {object} proto.PermissionSkipRequest
+// @Failure 404 {object} proto.Error
+// @Failure 500 {object} proto.Error
+// @Router /workspaces/{id}/permissions/skip [get]
func (c *controllerV1) handleGetWorkspacePermissionsSkip(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
skip, err := c.backend.GetPermissionsSkip(id)
@@ -11,8 +11,10 @@ import (
"runtime"
"strings"
+ _ "github.com/charmbracelet/crush/docs"
"github.com/charmbracelet/crush/internal/backend"
"github.com/charmbracelet/crush/internal/config"
+ httpswagger "github.com/swaggo/http-swagger/v2"
)
// ErrServerClosed is returned when the server is closed.
@@ -161,6 +163,7 @@ func NewServer(cfg *config.ConfigStore, network, address string) *Server {
mux.HandleFunc("GET /v1/workspaces/{id}/mcp/states", c.handleGetWorkspaceMCPStates)
mux.HandleFunc("POST /v1/workspaces/{id}/mcp/refresh-prompts", c.handlePostWorkspaceMCPRefreshPrompts)
mux.HandleFunc("POST /v1/workspaces/{id}/mcp/refresh-resources", c.handlePostWorkspaceMCPRefreshResources)
+ mux.Handle("/v1/docs/", httpswagger.WrapHandler)
s.h = &http.Server{
Protocols: &p,
Handler: s.loggingHandler(mux),
@@ -1,3 +1,13 @@
+// Package main is the entry point for the Crush CLI.
+//
+// @title Crush API
+// @version 1.0
+// @description Crush is a terminal-based AI coding assistant. This API is served over a Unix socket (or Windows named pipe) and provides programmatic access to workspaces, sessions, agents, LSP, MCP, and more.
+// @contact.name Charm
+// @contact.url https://charm.sh
+// @license.name MIT
+// @license.url https://github.com/charmbracelet/crush/blob/main/LICENSE
+// @BasePath /v1
package main
import (