Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7a72a49

Browse files
authored
feat: support opencode (#79)
1 parent 0876f7d commit 7a72a49

File tree

18 files changed

+3093
-24
lines changed

18 files changed

+3093
-24
lines changed

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ agentapi server -- goose
6565
```
6666

6767
> [!NOTE]
68-
> When using Codex, Gemini, Amp or CursorCLI, always specify the agent type explicitly (eg: `agentapi server --type=codex -- codex`), or message formatting may break.
68+
> When using Codex, Opencode, Gemini, Amp or CursorCLI, always specify the agent type explicitly (eg: `agentapi server --type=codex -- codex`), or message formatting may break.
6969
7070
An OpenAPI schema is available in [openapi.json](openapi.json).
7171

‎cmd/server/server.go‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,17 @@ import (
2323
type AgentType = msgfmt.AgentType
2424

2525
const (
26-
AgentTypeClaude AgentType = msgfmt.AgentTypeClaude
27-
AgentTypeGoose AgentType = msgfmt.AgentTypeGoose
28-
AgentTypeAider AgentType = msgfmt.AgentTypeAider
29-
AgentTypeCodex AgentType = msgfmt.AgentTypeCodex
30-
AgentTypeGemini AgentType = msgfmt.AgentTypeGemini
31-
AgentTypeAmp AgentType = msgfmt.AgentTypeAmp
32-
AgentTypeCursor AgentType = msgfmt.AgentTypeCursor
33-
AgentTypeAuggie AgentType = msgfmt.AgentTypeAuggie
34-
AgentTypeAmazonQ AgentType = msgfmt.AgentTypeAmazonQ
35-
AgentTypeCustom AgentType = msgfmt.AgentTypeCustom
26+
AgentTypeClaude AgentType = msgfmt.AgentTypeClaude
27+
AgentTypeGoose AgentType = msgfmt.AgentTypeGoose
28+
AgentTypeAider AgentType = msgfmt.AgentTypeAider
29+
AgentTypeCodex AgentType = msgfmt.AgentTypeCodex
30+
AgentTypeGemini AgentType = msgfmt.AgentTypeGemini
31+
AgentTypeAmp AgentType = msgfmt.AgentTypeAmp
32+
AgentTypeCursor AgentType = msgfmt.AgentTypeCursor
33+
AgentTypeAuggie AgentType = msgfmt.AgentTypeAuggie
34+
AgentTypeAmazonQ AgentType = msgfmt.AgentTypeAmazonQ
35+
AgentTypeOpencode AgentType = msgfmt.AgentTypeOpencode
36+
AgentTypeCustom AgentType = msgfmt.AgentTypeCustom
3637
)
3738

3839
// agentTypeAliases contains the mapping of possible input agent type strings to their canonical AgentType values
@@ -48,6 +49,7 @@ var agentTypeAliases = map[string]AgentType{
4849
"cursor-agent": AgentTypeCursor,
4950
"q": AgentTypeAmazonQ,
5051
"amazonq": AgentTypeAmazonQ,
52+
"opencode": AgentTypeOpencode,
5153
"custom": AgentTypeCustom,
5254
}
5355

‎cmd/server/server_test.go‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ func TestParseAgentType(t *testing.T) {
6767
agentTypeVar: "",
6868
want: AgentTypeAmazonQ,
6969
},
70+
{
71+
firstArg: "opencode",
72+
agentTypeVar: "",
73+
want: AgentTypeOpencode,
74+
},
7075
{
7176
firstArg: "auggie",
7277
agentTypeVar: "",
@@ -117,6 +122,11 @@ func TestParseAgentType(t *testing.T) {
117122
agentTypeVar: "q",
118123
want: AgentTypeAmazonQ,
119124
},
125+
{
126+
firstArg: "claude",
127+
agentTypeVar: "opencode",
128+
want: AgentTypeOpencode,
129+
},
120130
{
121131
firstArg: "claude",
122132
agentTypeVar: "cursor-agent",

‎lib/httpapi/server.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ func NewServer(ctx context.Context, config ServerConfig) (*Server, error) {
196196
return mf.FormatAgentMessage(config.AgentType, message, userInput)
197197
}
198198
conversation := st.NewConversation(ctx, st.ConversationConfig{
199-
AgentIO: config.Process,
199+
AgentType: config.AgentType,
200+
AgentIO: config.Process,
200201
GetTime: func() time.Time {
201202
return time.Now()
202203
},

‎lib/msgfmt/message_box.go‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,22 @@ func removeCodexInputBox(msg string) string {
6060
}
6161
return strings.Join(lines, "\n")
6262
}
63+
64+
func removeOpencodeMessageBox(msg string) string {
65+
lines := strings.Split(msg, "\n")
66+
// Check the last 3 lines for
67+
//
68+
// ┃ ┃
69+
// ┃ > ┃
70+
// ┃ ┃
71+
// We only check for the first ┃ and then an empty line above it - as sometimes the full input block does not load within a snapshot,
72+
// this leads to displaying a bunch of newlines.
73+
for i := len(lines) - 1; i >= 1; i-- {
74+
if strings.TrimSpace(lines[i-1]) == "" &&
75+
strings.ReplaceAll(lines[i], " ", "") == "┃┃" {
76+
lines = lines[:i-1]
77+
break
78+
}
79+
}
80+
return strings.Join(lines, "\n")
81+
}

‎lib/msgfmt/msgfmt.go‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,13 @@ func RemoveUserInput(msgRaw string, userInputRaw string, agentType AgentType) st
196196
if idx, found := skipTrailingInputBoxLine(msgLines, lastUserInputLineIdx, "┘", "└"); found {
197197
lastUserInputLineIdx = idx
198198
}
199+
} else if agentType == AgentTypeOpencode {
200+
// skip +2 lines after the input
201+
// ┃ jkmr (08:46 PM) ┃
202+
// ┃ ┃
203+
if lastUserInputLineIdx+2 < len(msgLines) {
204+
lastUserInputLineIdx += 2
205+
}
199206
}
200207

201208
return strings.Join(msgLines[lastUserInputLineIdx+1:], "\n")
@@ -234,6 +241,7 @@ const (
234241
AgentTypeCursor AgentType = "cursor"
235242
AgentTypeAuggie AgentType = "auggie"
236243
AgentTypeAmazonQ AgentType = "amazonq"
244+
AgentTypeOpencode AgentType = "opencode"
237245
AgentTypeCustom AgentType = "custom"
238246
)
239247

@@ -251,6 +259,13 @@ func formatCodexMessage(message string, userInput string) string {
251259
return message
252260
}
253261

262+
func formatOpencodeMessage(message string, userInput string) string {
263+
message = RemoveUserInput(message, userInput, AgentTypeOpencode)
264+
message = removeOpencodeMessageBox(message)
265+
message = trimEmptyLines(message)
266+
return message
267+
}
268+
254269
func FormatAgentMessage(agentType AgentType, message string, userInput string) string {
255270
switch agentType {
256271
case AgentTypeClaude:
@@ -271,6 +286,8 @@ func FormatAgentMessage(agentType AgentType, message string, userInput string) s
271286
return formatGenericMessage(message, userInput, agentType)
272287
case AgentTypeAmazonQ:
273288
return formatGenericMessage(message, userInput, agentType)
289+
case AgentTypeOpencode:
290+
return formatOpencodeMessage(message, userInput)
274291
case AgentTypeCustom:
275292
return formatGenericMessage(message, userInput, agentType)
276293
default:

‎lib/msgfmt/msgfmt_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func TestTrimEmptyLines(t *testing.T) {
218218

219219
func TestFormatAgentMessage(t *testing.T) {
220220
dir := "testdata/format"
221-
agentTypes := []AgentType{AgentTypeClaude, AgentTypeGoose, AgentTypeAider, AgentTypeGemini, AgentTypeAmp, AgentTypeCodex, AgentTypeCursor, AgentTypeAuggie, AgentTypeAmazonQ, AgentTypeCustom}
221+
agentTypes := []AgentType{AgentTypeClaude, AgentTypeGoose, AgentTypeAider, AgentTypeGemini, AgentTypeAmp, AgentTypeCodex, AgentTypeCursor, AgentTypeAuggie, AgentTypeAmazonQ, AgentTypeOpencode, AgentTypeCustom}
222222
for _, agentType := range agentTypes {
223223
t.Run(string(agentType), func(t *testing.T) {
224224
cases, err := testdataDir.ReadDir(path.Join(dir, string(agentType)))
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
█▀▀█ █▀▀█ █▀▀ █▀▀▄ █▀▀ █▀▀█ █▀▀▄ █▀▀
2+
█░░█ █░░█ █▀▀ █░░█ █░░ █░░█ █░░█ █▀▀
3+
▀▀▀▀ █▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀▀ ▀▀▀ ▀▀▀
4+
v0.6.8
5+
6+
/new new session ctrl+x n
7+
/help show help ctrl+x h
8+
/share share session ctrl+x s
9+
/models list models ctrl+x m
10+
11+
12+
Grok Code is free for a limited time

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /