slash commands (#2157)

Co-authored-by: adamdotdevin <2363879+adamdottv@users.noreply.github.com>
This commit is contained in:
Dax
2025-08-22 17:04:28 -04:00
committed by GitHub
parent 74c1085103
commit 133fe41cd5
32 changed files with 874 additions and 69 deletions

View File

@@ -1,4 +1,4 @@
configured_endpoints: 39
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-be3e40e0bf7dde2bb15ff82d5d104418fb47fe335808a1aa6468b0be2210a88f.yml
openapi_spec_hash: c1bbb3ebd807656bd9f31a618077e76b
config_hash: eab3723c4c2232a6ba1821151259d6da
configured_endpoints: 41
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-d5200eaa145f567a58daa78941ab1141dd63f5f0cfe1596d5c9ecf12d34fea35.yml
openapi_spec_hash: abeb66291dc158f2cdc90bf9945e283e
config_hash: fb625e876313a9f8f31532348fa91f59

View File

@@ -70,6 +70,16 @@ Methods:
- <code title="get /config">client.Config.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#ConfigService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Config">Config</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
# Command
Response Types:
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Command">Command</a>
Methods:
- <code title="get /command">client.Command.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#CommandService.List">List</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Command">Command</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
# Session
Params Types:
@@ -106,6 +116,7 @@ Response Types:
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#ToolStateRunning">ToolStateRunning</a>
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#UserMessage">UserMessage</a>
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChatResponse">SessionChatResponse</a>
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionCommandResponse">SessionCommandResponse</a>
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessageResponse">SessionMessageResponse</a>
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessagesResponse">SessionMessagesResponse</a>
@@ -118,6 +129,7 @@ Methods:
- <code title="post /session/{id}/abort">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Abort">Abort</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
- <code title="post /session/{id}/message">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Chat">Chat</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChatParams">SessionChatParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChatResponse">SessionChatResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
- <code title="get /session/{id}/children">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Children">Children</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
- <code title="post /session/{id}/command">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Command">Command</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionCommandParams">SessionCommandParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionCommandResponse">SessionCommandResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
- <code title="get /session/{id}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
- <code title="post /session/{id}/init">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Init">Init</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionInitParams">SessionInitParams</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
- <code title="get /session/{id}/message/{messageID}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Message">Message</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, messageID <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessageResponse">SessionMessageResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>

View File

@@ -21,6 +21,7 @@ type Client struct {
Find *FindService
File *FileService
Config *ConfigService
Command *CommandService
Session *SessionService
Tui *TuiService
}
@@ -49,6 +50,7 @@ func NewClient(opts ...option.RequestOption) (r *Client) {
r.Find = NewFindService(opts...)
r.File = NewFileService(opts...)
r.Config = NewConfigService(opts...)
r.Command = NewCommandService(opts...)
r.Session = NewSessionService(opts...)
r.Tui = NewTuiService(opts...)

View File

@@ -0,0 +1,67 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
package opencode
import (
"context"
"net/http"
"github.com/sst/opencode-sdk-go/internal/apijson"
"github.com/sst/opencode-sdk-go/internal/requestconfig"
"github.com/sst/opencode-sdk-go/option"
)
// CommandService contains methods and other services that help with interacting
// with the opencode API.
//
// Note, unlike clients, this service does not read variables from the environment
// automatically. You should not instantiate this service directly, and instead use
// the [NewCommandService] method instead.
type CommandService struct {
Options []option.RequestOption
}
// NewCommandService generates a new service that applies the given options to each
// request. These options are applied after the parent client's options (if there
// is one), and before any request-specific options.
func NewCommandService(opts ...option.RequestOption) (r *CommandService) {
r = &CommandService{}
r.Options = opts
return
}
// List all commands
func (r *CommandService) List(ctx context.Context, opts ...option.RequestOption) (res *[]Command, err error) {
opts = append(r.Options[:], opts...)
path := "command"
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...)
return
}
type Command struct {
Name string `json:"name,required"`
Template string `json:"template,required"`
Agent string `json:"agent"`
Description string `json:"description"`
Model string `json:"model"`
JSON commandJSON `json:"-"`
}
// commandJSON contains the JSON metadata for the struct [Command]
type commandJSON struct {
Name apijson.Field
Template apijson.Field
Agent apijson.Field
Description apijson.Field
Model apijson.Field
raw string
ExtraFields map[string]apijson.Field
}
func (r *Command) UnmarshalJSON(data []byte) (err error) {
return apijson.UnmarshalRoot(data, r)
}
func (r commandJSON) RawJSON() string {
return r.raw
}

View File

@@ -0,0 +1,36 @@
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
package opencode_test
import (
"context"
"errors"
"os"
"testing"
"github.com/sst/opencode-sdk-go"
"github.com/sst/opencode-sdk-go/internal/testutil"
"github.com/sst/opencode-sdk-go/option"
)
func TestCommandList(t *testing.T) {
t.Skip("skipped: tests are disabled for the time being")
baseURL := "http://localhost:4010"
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
baseURL = envURL
}
if !testutil.CheckTestServer(t, baseURL) {
return
}
client := opencode.NewClient(
option.WithBaseURL(baseURL),
)
_, err := client.Command.List(context.TODO())
if err != nil {
var apierr *opencode.Error
if errors.As(err, &apierr) {
t.Log(string(apierr.DumpRequest(true)))
}
t.Fatalf("err should be nil: %s", err.Error())
}
}

View File

@@ -49,7 +49,8 @@ type Config struct {
// automatically
Autoshare bool `json:"autoshare"`
// Automatically update to the latest version
Autoupdate bool `json:"autoupdate"`
Autoupdate bool `json:"autoupdate"`
Command map[string]ConfigCommand `json:"command"`
// Disable providers that are loaded automatically
DisabledProviders []string `json:"disabled_providers"`
Experimental ConfigExperimental `json:"experimental"`
@@ -94,6 +95,7 @@ type configJSON struct {
Agent apijson.Field
Autoshare apijson.Field
Autoupdate apijson.Field
Command apijson.Field
DisabledProviders apijson.Field
Experimental apijson.Field
Formatter apijson.Field
@@ -664,6 +666,32 @@ func (r ConfigAgentPlanPermissionWebfetch) IsKnown() bool {
return false
}
type ConfigCommand struct {
Template string `json:"template,required"`
Agent string `json:"agent"`
Description string `json:"description"`
Model string `json:"model"`
JSON configCommandJSON `json:"-"`
}
// configCommandJSON contains the JSON metadata for the struct [ConfigCommand]
type configCommandJSON struct {
Template apijson.Field
Agent apijson.Field
Description apijson.Field
Model apijson.Field
raw string
ExtraFields map[string]apijson.Field
}
func (r *ConfigCommand) UnmarshalJSON(data []byte) (err error) {
return apijson.UnmarshalRoot(data, r)
}
func (r configCommandJSON) RawJSON() string {
return r.raw
}
type ConfigExperimental struct {
Hook ConfigExperimentalHook `json:"hook"`
JSON configExperimentalJSON `json:"-"`

View File

@@ -114,6 +114,18 @@ func (r *SessionService) Children(ctx context.Context, id string, opts ...option
return
}
// Send a new command to a session
func (r *SessionService) Command(ctx context.Context, id string, body SessionCommandParams, opts ...option.RequestOption) (res *SessionCommandResponse, err error) {
opts = append(r.Options[:], opts...)
if id == "" {
err = errors.New("missing required id parameter")
return
}
path := fmt.Sprintf("session/%s/command", id)
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
return
}
// Get session
func (r *SessionService) Get(ctx context.Context, id string, opts ...option.RequestOption) (res *Session, err error) {
opts = append(r.Options[:], opts...)
@@ -2301,6 +2313,29 @@ func (r sessionChatResponseJSON) RawJSON() string {
return r.raw
}
type SessionCommandResponse struct {
Info AssistantMessage `json:"info,required"`
Parts []Part `json:"parts,required"`
JSON sessionCommandResponseJSON `json:"-"`
}
// sessionCommandResponseJSON contains the JSON metadata for the struct
// [SessionCommandResponse]
type sessionCommandResponseJSON struct {
Info apijson.Field
Parts apijson.Field
raw string
ExtraFields map[string]apijson.Field
}
func (r *SessionCommandResponse) UnmarshalJSON(data []byte) (err error) {
return apijson.UnmarshalRoot(data, r)
}
func (r sessionCommandResponseJSON) RawJSON() string {
return r.raw
}
type SessionMessageResponse struct {
Info Message `json:"info,required"`
Parts []Part `json:"parts,required"`
@@ -2419,6 +2454,18 @@ func (r SessionChatParamsPartsType) IsKnown() bool {
return false
}
type SessionCommandParams struct {
Arguments param.Field[string] `json:"arguments,required"`
Command param.Field[string] `json:"command,required"`
Agent param.Field[string] `json:"agent"`
MessageID param.Field[string] `json:"messageID"`
Model param.Field[string] `json:"model"`
}
func (r SessionCommandParams) MarshalJSON() (data []byte, err error) {
return apijson.MarshalRoot(r)
}
type SessionInitParams struct {
MessageID param.Field[string] `json:"messageID,required"`
ModelID param.Field[string] `json:"modelID,required"`

View File

@@ -199,6 +199,38 @@ func TestSessionChildren(t *testing.T) {
}
}
func TestSessionCommandWithOptionalParams(t *testing.T) {
t.Skip("skipped: tests are disabled for the time being")
baseURL := "http://localhost:4010"
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
baseURL = envURL
}
if !testutil.CheckTestServer(t, baseURL) {
return
}
client := opencode.NewClient(
option.WithBaseURL(baseURL),
)
_, err := client.Session.Command(
context.TODO(),
"id",
opencode.SessionCommandParams{
Arguments: opencode.F("arguments"),
Command: opencode.F("command"),
Agent: opencode.F("agent"),
MessageID: opencode.F("msg"),
Model: opencode.F("model"),
},
)
if err != nil {
var apierr *opencode.Error
if errors.As(err, &apierr) {
t.Log(string(apierr.DumpRequest(true)))
}
t.Fatalf("err should be nil: %s", err.Error())
}
}
func TestSessionGet(t *testing.T) {
t.Skip("skipped: tests are disabled for the time being")
baseURL := "http://localhost:4010"

View File

@@ -39,6 +39,8 @@ import type {
SessionChatResponses,
SessionMessageData,
SessionMessageResponses,
SessionCommandData,
SessionCommandResponses,
SessionShellData,
SessionShellResponses,
SessionRevertData,
@@ -47,6 +49,8 @@ import type {
SessionUnrevertResponses,
PostSessionByIdPermissionsByPermissionIdData,
PostSessionByIdPermissionsByPermissionIdResponses,
CommandListData,
CommandListResponses,
ConfigProvidersData,
ConfigProvidersResponses,
FindTextData,
@@ -355,6 +359,20 @@ class Session extends _HeyApiClient {
})
}
/**
* Send a new command to a session
*/
public command<ThrowOnError extends boolean = false>(options: Options<SessionCommandData, ThrowOnError>) {
return (options.client ?? this._client).post<SessionCommandResponses, unknown, ThrowOnError>({
url: "/session/{id}/command",
...options,
headers: {
"Content-Type": "application/json",
...options.headers,
},
})
}
/**
* Run a shell command
*/
@@ -394,6 +412,18 @@ class Session extends _HeyApiClient {
}
}
class Command extends _HeyApiClient {
/**
* List all commands
*/
public list<ThrowOnError extends boolean = false>(options?: Options<CommandListData, ThrowOnError>) {
return (options?.client ?? this._client).get<CommandListResponses, unknown, ThrowOnError>({
url: "/command",
...options,
})
}
}
class Find extends _HeyApiClient {
/**
* Find text in files
@@ -592,6 +622,7 @@ export class OpencodeClient extends _HeyApiClient {
app = new App({ client: this._client })
config = new Config({ client: this._client })
session = new Session({ client: this._client })
command = new Command({ client: this._client })
find = new Find({ client: this._client })
file = new File({ client: this._client })
tui = new Tui({ client: this._client })

View File

@@ -585,6 +585,14 @@ export type Config = {
*/
scroll_speed: number
}
command?: {
[key: string]: {
template: string
description?: string
agent?: string
model?: string
}
}
plugin?: Array<string>
snapshot?: boolean
/**
@@ -1110,6 +1118,14 @@ export type AgentPartInput = {
}
}
export type Command = {
name: string
description?: string
agent?: string
model?: string
template: string
}
export type Symbol = {
name: string
kind: number
@@ -1563,6 +1579,36 @@ export type SessionMessageResponses = {
export type SessionMessageResponse = SessionMessageResponses[keyof SessionMessageResponses]
export type SessionCommandData = {
body?: {
messageID?: string
agent?: string
model?: string
arguments: string
command: string
}
path: {
/**
* Session ID
*/
id: string
}
query?: never
url: "/session/{id}/command"
}
export type SessionCommandResponses = {
/**
* Created message
*/
200: {
info: AssistantMessage
parts: Array<Part>
}
}
export type SessionCommandResponse = SessionCommandResponses[keyof SessionCommandResponses]
export type SessionShellData = {
body?: {
agent: string
@@ -1648,6 +1694,22 @@ export type PostSessionByIdPermissionsByPermissionIdResponses = {
export type PostSessionByIdPermissionsByPermissionIdResponse =
PostSessionByIdPermissionsByPermissionIdResponses[keyof PostSessionByIdPermissionsByPermissionIdResponses]
export type CommandListData = {
body?: never
path?: never
query?: never
url: "/command"
}
export type CommandListResponses = {
/**
* List of commands
*/
200: Array<Command>
}
export type CommandListResponse = CommandListResponses[keyof CommandListResponses]
export type ConfigProvidersData = {
body?: never
path?: never

View File

@@ -85,6 +85,12 @@ resources:
methods:
get: get /config
command:
models:
command: Command
methods:
list: get /command
session:
models:
session: Session
@@ -126,6 +132,7 @@ resources:
message: get /session/{id}/message/{messageID}
messages: get /session/{id}/message
chat: post /session/{id}/message
command: post /session/{id}/command
shell: post /session/{id}/shell
update: patch /session/{id}
revert: post /session/{id}/revert