oh-my-opencode 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ko.md CHANGED
@@ -1,4 +1,29 @@
1
- [English](README.md) | 한국어
1
+ <!-- <CENTERED SECTION FOR GITHUB DISPLAY> -->
2
+
3
+ <div align="center">
4
+
5
+ [![Oh My OpenCode](./.github/assets/hero.jpg)](https://github.com/code-yeongyu/oh-my-opencode#oh-my-opencode)
6
+
7
+ [![Preview](./.github/assets/preview.png)](https://github.com/code-yeongyu/oh-my-opencode#oh-my-opencode)
8
+
9
+ </div>
10
+
11
+ > `oh-my-opencode` 를 설치하세요. 약 빤 것 처럼 코딩하세요. 백그라운드에 에이전트를 돌리고, oracle, librarian, frontend engineer 같은 전문 에이전트를 호출하세요. 정성스레 빚은 LSP/AST 도구, 엄선된 MCP, 완전한 Claude Code 호환 레이어를 오로지 한 줄로 누리세요.
12
+
13
+ <div align="center">
14
+
15
+ [![GitHub Release](https://img.shields.io/github/v/release/code-yeongyu/oh-my-opencode?color=369eff&labelColor=black&logo=github&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/releases)
16
+ [![GitHub Contributors](https://img.shields.io/github/contributors/code-yeongyu/oh-my-opencode?color=c4f042&labelColor=black&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/graphs/contributors)
17
+ [![GitHub Forks](https://img.shields.io/github/forks/code-yeongyu/oh-my-opencode?color=8ae8ff&labelColor=black&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/network/members)
18
+ [![GitHub Stars](https://img.shields.io/github/stars/code-yeongyu/oh-my-opencode?color=ffcb47&labelColor=black&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/stargazers)
19
+ [![GitHub Issues](https://img.shields.io/github/issues/code-yeongyu/oh-my-opencode?color=ff80eb&labelColor=black&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/issues)
20
+ [![License](https://img.shields.io/badge/license-MIT-white?labelColor=black&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/blob/master/LICENSE)
21
+
22
+ [English](README.md) | [한국어](README.ko.md)
23
+
24
+ </div>
25
+
26
+ <!-- </CENTERED SECTION FOR GITHUB DISPLAY> -->
2
27
 
3
28
  ## 목차
4
29
 
@@ -74,16 +99,20 @@ OpenCode 가 낭만이 사라진것같은 오늘날의 시대에, 당신에게
74
99
 
75
100
  ### 10분의 투자로 OhMyOpenCode 가 해줄 수 있는것
76
101
 
77
- 1. **백그라운드 태스크로 Gemini 3 Pro 프론트엔드를 작성하게 시켜두는 동안, Claude Opus 4.5 가 백엔드를 작성하고, 디버깅하다 막히면 GPT 5.2 에게 도움을 받습니다. 프론트엔드 구현이 완료되었다고 보고받으면, 이를 다시 확인하고 일하게 만들 수 있습니다.**
102
+ 그저 설치하면, 아래와 같은 워크플로우로 수도 있습니다.
103
+
104
+ 1. 백그라운드 태스크로 Gemini 3 Pro 가 프론트엔드를 작성하게 시켜두는 동안, Claude Opus 4.5 가 백엔드를 작성하고, 디버깅하다 막히면 GPT 5.2 에게 도움을 받습니다. 프론트엔드 구현이 완료되었다고 보고받으면, 이를 다시 확인하고 일하게 만들 수 있습니다.
78
105
  2. 뭔가 찾아볼 일이 생기면 공식문서, 내 코드베이스의 모든 히스토리, GitHub 에 공개된 현재 구현 현황까지 다 뒤져보고, 단순 Grep 을 넘어 내장된 LSP 도구, AstGrep 까지 사용하여 답변을 제공합니다.
79
106
  3. LLM 에게 일을 맡길때에 큰 컨텍스트에 대한 걱정은 더 이상 하지마세요. 제가 하겠습니다.
80
107
  - OhMyOpenCode 가 여러 에이전트를 적극 활용하도록 하여 컨텍스트 관리에 관한 부담을 줄입니다.
81
108
  - **당신의 에이전트는 이제 개발팀 리드입니다. 당신은 이제 AI Manager 입니다.**
82
109
  4. 하기로 약속 한 일을 완수 할 때 까지 멈추지 않습니다.
110
+ 5. 이 프로젝트에 자세히 알기 싫다고요? 괜찮습니다. 그냥 'ultrathink' 라고 치세요.
83
111
 
84
-
85
- - 복잡하고 일도 그냥 시키세요.
86
- - 프롬프트에서 "ultrawork" 키워드를 감지하면, 위의 모든 과정을 알아서 진행합니다.
112
+ 주의: 이걸 설치한다고 갑자기 OpenCode 가 이렇게 동작한다는 것은 아닙니다. 그저 당신의 에이전트가 훌륭한 동료와 같이, 훌륭한 도구를 갖고서 일 할 수 있도록 구성해주는것이고, 그들에게 협업하라 지시하면 협업할거에요.
113
+ 모든 과정은 당신이 완전히 컨트롤 할 수 있습니다.
114
+ ultrathink 통해 자동으로 동작하게 있지만, 그렇지 않을수도 있습니다. 이 프로젝트가 당신의 AI 에이전트 워크플로우를 제시하지는 않습니다.
115
+ 이 프로젝트는 그저 당신의 에이전트에게 좋은 동료를 소개시켜주고, 좋은 도구를 쥐어주는 것 뿐입니다.
87
116
 
88
117
  ## 설치
89
118
 
package/README.md CHANGED
@@ -1,9 +1,35 @@
1
+ <!-- <CENTERED SECTION FOR GITHUB DISPLAY> -->
2
+
3
+ <div align="center">
4
+
5
+ [![Oh My OpenCode](./.github/assets/hero.jpg)](https://github.com/code-yeongyu/oh-my-opencode#oh-my-opencode)
6
+
7
+ [![Preview](./.github/assets/preview.png)](https://github.com/code-yeongyu/oh-my-opencode#oh-my-opencode)
8
+
9
+
10
+ </div>
11
+
12
+ > This is coding on steroids—`oh-my-opencode` in action. Run background agents, call specialized agents like oracle, librarian, and frontend engineer. Use crafted LSP/AST tools, curated MCPs, and a full Claude Code compatibility layer.
13
+
14
+ <div align="center">
15
+
16
+ [![GitHub Release](https://img.shields.io/github/v/release/code-yeongyu/oh-my-opencode?color=369eff&labelColor=black&logo=github&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/releases)
17
+ [![GitHub Contributors](https://img.shields.io/github/contributors/code-yeongyu/oh-my-opencode?color=c4f042&labelColor=black&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/graphs/contributors)
18
+ [![GitHub Forks](https://img.shields.io/github/forks/code-yeongyu/oh-my-opencode?color=8ae8ff&labelColor=black&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/network/members)
19
+ [![GitHub Stars](https://img.shields.io/github/stars/code-yeongyu/oh-my-opencode?color=ffcb47&labelColor=black&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/stargazers)
20
+ [![GitHub Issues](https://img.shields.io/github/issues/code-yeongyu/oh-my-opencode?color=ff80eb&labelColor=black&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/issues)
21
+ [![License](https://img.shields.io/badge/license-MIT-white?labelColor=black&style=flat-square)](https://github.com/code-yeongyu/oh-my-opencode/blob/master/LICENSE)
22
+
1
23
  [English](README.md) | [한국어](README.ko.md)
2
24
 
25
+ </div>
26
+
27
+ <!-- </CENTERED SECTION FOR GITHUB DISPLAY> -->
28
+
3
29
  ## Contents
4
30
 
5
31
  - [Oh My OpenCode](#oh-my-opencode)
6
- - [Skip Reading This](#skip-reading-this)
32
+ - [Just Skip Reading This Readme](#just-skip-reading-this-readme)
7
33
  - [It's the Age of Agents](#its-the-age-of-agents)
8
34
  - [10 Minutes to Unlock](#10-minutes-to-unlock)
9
35
  - [Installation](#installation)
@@ -64,7 +90,7 @@ I've fixed that.
64
90
  Even if you're not a hacker, invest a few minutes. Multiply your skills and productivity.
65
91
  Hand this doc to an agent and let them set it up.
66
92
 
67
- ## Skip Reading This
93
+ ## Just Skip Reading This Readme
68
94
 
69
95
  ### It's the Age of Agents
70
96
  - **Just paste this link into Claude Code / AmpCode / Factory Droid / Cursor and ask it to explain.**
@@ -73,13 +99,20 @@ Hand this doc to an agent and let them set it up.
73
99
 
74
100
  ### 10 Minutes to Unlock
75
101
 
76
- 1. **While Gemini 3 Pro writes the frontend as a background task, Claude Opus 4.5 handles the backend. Stuck debugging? Call GPT 5.2 for help. When the frontend reports done, verify and ship.**
102
+ Just by installing this, you make your agents to work like:
103
+
104
+ 1. While Gemini 3 Pro writes the frontend as a background task, Claude Opus 4.5 handles the backend. Stuck debugging? Call GPT 5.2 for help. When the frontend reports done, verify and ship.
77
105
  2. Need to look something up? It scours official docs, your entire codebase history, and public GitHub implementations—using not just grep but built-in LSP tools and AST-Grep.
78
106
  3. Stop worrying about context management when delegating to LLMs. I've got it covered.
79
107
  - OhMyOpenCode aggressively leverages multiple agents to lighten the context load.
80
108
  - **Your agent is now the dev team lead. You're the AI Manager.**
81
109
  4. It doesn't stop until the job is done.
110
+ 5. Don't want to dive deep into this project? No problem. Just type 'ultrathink'.
82
111
 
112
+ Note: Installing this doesn't magically make OpenCode behave this way. Above explanation is like "you can utilize even like this". It simply equips your agent with excellent teammates and powerful tools—tell them to collaborate and they will.
113
+ You're in full control.
114
+ You can enable automatic behavior via ultrathink, but you don't have to. This project doesn't dictate your AI agent workflow.
115
+ It simply introduces your agent to great colleagues and puts better tools in their hands.
83
116
 
84
117
  - Throw complex, massive tasks at it.
85
118
  - Drop the "ultrawork" keyword in your prompt and it handles everything automatically.
@@ -570,3 +603,5 @@ I have no affiliation with any project or model mentioned here. This is purely p
570
603
  - If you're on [1.0.132](https://github.com/sst/opencode/releases/tag/v1.0.132) or older, an OpenCode bug may break config.
571
604
  - [The fix](https://github.com/sst/opencode/pull/5040) was merged after 1.0.132—use a newer version.
572
605
  - Fun fact: That PR was discovered and fixed thanks to OhMyOpenCode's Librarian, Explore, and Oracle setup.
606
+
607
+ *Special thanks to @junhoyeo for this amazing hero image.*
package/dist/index.js CHANGED
@@ -8254,6 +8254,10 @@ async function attemptFetch(options) {
8254
8254
  signal: init.signal
8255
8255
  });
8256
8256
  debugLog6(`[RESP] status=${response.status} content-type=${response.headers.get("content-type") ?? ""} url=${response.url}`);
8257
+ if (response.status === 401) {
8258
+ debugLog6(`[401] Unauthorized response detected, signaling token refresh needed`);
8259
+ return "needs-refresh";
8260
+ }
8257
8261
  if (response.status === 403) {
8258
8262
  try {
8259
8263
  const text = await response.clone().text();
@@ -8403,45 +8407,99 @@ function createAntigravityFetch(getAuth, client, providerId, clientId, clientSec
8403
8407
  const sessionId = getOrCreateSessionId(fetchInstanceId);
8404
8408
  const thoughtSignature = getThoughtSignature(fetchInstanceId);
8405
8409
  debugLog6(`[TSIG][GET] sessionId=${sessionId}, signature=${thoughtSignature ? thoughtSignature.substring(0, 20) + "..." : "none"}`);
8406
- for (let i = 0;i < maxEndpoints; i++) {
8407
- const endpoint = ANTIGRAVITY_ENDPOINT_FALLBACKS[i];
8408
- const response = await attemptFetch({
8409
- endpoint,
8410
- url,
8411
- init,
8412
- accessToken: cachedTokens.access_token,
8413
- projectId,
8414
- sessionId,
8415
- modelName,
8416
- thoughtSignature
8410
+ let hasRefreshedFor401 = false;
8411
+ const executeWithEndpoints = async () => {
8412
+ for (let i = 0;i < maxEndpoints; i++) {
8413
+ const endpoint = ANTIGRAVITY_ENDPOINT_FALLBACKS[i];
8414
+ const response = await attemptFetch({
8415
+ endpoint,
8416
+ url,
8417
+ init,
8418
+ accessToken: cachedTokens.access_token,
8419
+ projectId,
8420
+ sessionId,
8421
+ modelName,
8422
+ thoughtSignature
8423
+ });
8424
+ if (response === "pass-through") {
8425
+ debugLog6("Non-string body detected, passing through with auth headers");
8426
+ const headersWithAuth = {
8427
+ ...init.headers,
8428
+ Authorization: `Bearer ${cachedTokens.access_token}`
8429
+ };
8430
+ return fetch(url, { ...init, headers: headersWithAuth });
8431
+ }
8432
+ if (response === "needs-refresh") {
8433
+ if (hasRefreshedFor401) {
8434
+ debugLog6("[401] Already refreshed once, returning unauthorized error");
8435
+ return new Response(JSON.stringify({
8436
+ error: {
8437
+ message: "Authentication failed after token refresh",
8438
+ type: "unauthorized",
8439
+ code: "token_refresh_failed"
8440
+ }
8441
+ }), {
8442
+ status: 401,
8443
+ statusText: "Unauthorized",
8444
+ headers: { "Content-Type": "application/json" }
8445
+ });
8446
+ }
8447
+ debugLog6("[401] Refreshing token and retrying...");
8448
+ hasRefreshedFor401 = true;
8449
+ try {
8450
+ const newTokens = await refreshAccessToken(refreshParts.refreshToken, clientId, clientSecret);
8451
+ cachedTokens = {
8452
+ type: "antigravity",
8453
+ access_token: newTokens.access_token,
8454
+ refresh_token: newTokens.refresh_token,
8455
+ expires_in: newTokens.expires_in,
8456
+ timestamp: Date.now()
8457
+ };
8458
+ clearProjectContextCache();
8459
+ const formattedRefresh = formatTokenForStorage(newTokens.refresh_token, refreshParts.projectId || "", refreshParts.managedProjectId);
8460
+ await client.set(providerId, {
8461
+ access: newTokens.access_token,
8462
+ refresh: formattedRefresh,
8463
+ expires: Date.now() + newTokens.expires_in * 1000
8464
+ });
8465
+ debugLog6("[401] Token refreshed, retrying request...");
8466
+ return executeWithEndpoints();
8467
+ } catch (refreshError) {
8468
+ debugLog6(`[401] Token refresh failed: ${refreshError instanceof Error ? refreshError.message : "Unknown error"}`);
8469
+ return new Response(JSON.stringify({
8470
+ error: {
8471
+ message: `Token refresh failed: ${refreshError instanceof Error ? refreshError.message : "Unknown error"}`,
8472
+ type: "unauthorized",
8473
+ code: "token_refresh_failed"
8474
+ }
8475
+ }), {
8476
+ status: 401,
8477
+ statusText: "Unauthorized",
8478
+ headers: { "Content-Type": "application/json" }
8479
+ });
8480
+ }
8481
+ }
8482
+ if (response) {
8483
+ debugLog6(`Success with endpoint: ${endpoint}`);
8484
+ const transformedResponse = await transformResponseWithThinking(response, modelName || "", fetchInstanceId);
8485
+ return transformedResponse;
8486
+ }
8487
+ }
8488
+ const errorMessage = `All Antigravity endpoints failed after ${maxEndpoints} attempts`;
8489
+ debugLog6(errorMessage);
8490
+ return new Response(JSON.stringify({
8491
+ error: {
8492
+ message: errorMessage,
8493
+ type: "endpoint_failure",
8494
+ code: "all_endpoints_failed"
8495
+ }
8496
+ }), {
8497
+ status: 503,
8498
+ statusText: "Service Unavailable",
8499
+ headers: { "Content-Type": "application/json" }
8417
8500
  });
8418
- if (response === "pass-through") {
8419
- debugLog6("Non-string body detected, passing through with auth headers");
8420
- const headersWithAuth = {
8421
- ...init.headers,
8422
- Authorization: `Bearer ${cachedTokens.access_token}`
8423
- };
8424
- return fetch(url, { ...init, headers: headersWithAuth });
8425
- }
8426
- if (response) {
8427
- debugLog6(`Success with endpoint: ${endpoint}`);
8428
- const transformedResponse = await transformResponseWithThinking(response, modelName || "", fetchInstanceId);
8429
- return transformedResponse;
8430
- }
8431
- }
8432
- const errorMessage = `All Antigravity endpoints failed after ${maxEndpoints} attempts`;
8433
- debugLog6(errorMessage);
8434
- return new Response(JSON.stringify({
8435
- error: {
8436
- message: errorMessage,
8437
- type: "endpoint_failure",
8438
- code: "all_endpoints_failed"
8439
- }
8440
- }), {
8441
- status: 503,
8442
- statusText: "Service Unavailable",
8443
- headers: { "Content-Type": "application/json" }
8444
- });
8501
+ };
8502
+ return executeWithEndpoints();
8445
8503
  };
8446
8504
  }
8447
8505
  // src/auth/antigravity/plugin.ts
@@ -24181,14 +24239,17 @@ function createBackgroundTask(manager) {
24181
24239
  args: {
24182
24240
  description: tool.schema.string().describe("Short task description (shown in status)"),
24183
24241
  prompt: tool.schema.string().describe("Full detailed prompt for the agent"),
24184
- agent: tool.schema.string().describe("Agent type to use (any agent allowed)")
24242
+ agent: tool.schema.string().describe("Agent type to use (any registered agent)")
24185
24243
  },
24186
24244
  async execute(args, toolContext) {
24245
+ if (!args.agent || args.agent.trim() === "") {
24246
+ return `\u274C Agent parameter is required. Please specify which agent to use (e.g., "explore", "librarian", "build", etc.)`;
24247
+ }
24187
24248
  try {
24188
24249
  const task = await manager.launch({
24189
24250
  description: args.description,
24190
24251
  prompt: args.prompt,
24191
- agent: args.agent,
24252
+ agent: args.agent.trim(),
24192
24253
  parentSessionID: toolContext.sessionID,
24193
24254
  parentMessageID: toolContext.messageID
24194
24255
  });
@@ -24507,17 +24568,34 @@ async function executeSync(args, toolContext, ctx) {
24507
24568
  }
24508
24569
  log(`[call_omo_agent] Sending prompt to session ${sessionID}`);
24509
24570
  log(`[call_omo_agent] Prompt text:`, args.prompt.substring(0, 100));
24510
- await ctx.client.session.prompt({
24511
- path: { id: sessionID },
24512
- body: {
24513
- agent: args.subagent_type,
24514
- tools: {
24515
- task: false,
24516
- call_omo_agent: false
24517
- },
24518
- parts: [{ type: "text", text: args.prompt }]
24571
+ try {
24572
+ await ctx.client.session.prompt({
24573
+ path: { id: sessionID },
24574
+ body: {
24575
+ agent: args.subagent_type,
24576
+ tools: {
24577
+ task: false,
24578
+ call_omo_agent: false
24579
+ },
24580
+ parts: [{ type: "text", text: args.prompt }]
24581
+ }
24582
+ });
24583
+ } catch (error45) {
24584
+ const errorMessage = error45 instanceof Error ? error45.message : String(error45);
24585
+ log(`[call_omo_agent] Prompt error:`, errorMessage);
24586
+ if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
24587
+ return `Error: Agent "${args.subagent_type}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.
24588
+
24589
+ <task_metadata>
24590
+ session_id: ${sessionID}
24591
+ </task_metadata>`;
24519
24592
  }
24520
- });
24593
+ return `Error: Failed to send prompt: ${errorMessage}
24594
+
24595
+ <task_metadata>
24596
+ session_id: ${sessionID}
24597
+ </task_metadata>`;
24598
+ }
24521
24599
  log(`[call_omo_agent] Prompt sent, fetching messages...`);
24522
24600
  const messagesResult = await ctx.client.session.messages({
24523
24601
  path: { id: sessionID }
@@ -24681,6 +24759,9 @@ class BackgroundManager {
24681
24759
  this.directory = ctx.directory;
24682
24760
  }
24683
24761
  async launch(input) {
24762
+ if (!input.agent || input.agent.trim() === "") {
24763
+ throw new Error("Agent parameter is required");
24764
+ }
24684
24765
  const createResult = await this.client.session.create({
24685
24766
  body: {
24686
24767
  parentID: input.parentSessionID,
@@ -24708,7 +24789,7 @@ class BackgroundManager {
24708
24789
  };
24709
24790
  this.tasks.set(task.id, task);
24710
24791
  this.startPolling();
24711
- log("[background-agent] Launching task:", { taskId: task.id, sessionID });
24792
+ log("[background-agent] Launching task:", { taskId: task.id, sessionID, agent: input.agent });
24712
24793
  this.client.session.promptAsync({
24713
24794
  path: { id: sessionID },
24714
24795
  body: {
@@ -24726,8 +24807,15 @@ class BackgroundManager {
24726
24807
  const existingTask = this.findBySession(sessionID);
24727
24808
  if (existingTask) {
24728
24809
  existingTask.status = "error";
24729
- existingTask.error = String(error45);
24810
+ const errorMessage = error45 instanceof Error ? error45.message : String(error45);
24811
+ if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
24812
+ existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`;
24813
+ } else {
24814
+ existingTask.error = errorMessage;
24815
+ }
24730
24816
  existingTask.completedAt = new Date;
24817
+ this.markForNotification(existingTask);
24818
+ this.notifyParentSession(existingTask);
24731
24819
  }
24732
24820
  });
24733
24821
  return task;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "OpenCode plugin - custom agents (oracle, librarian) and enhanced features",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",