oh-my-opencode 1.0.0 → 1.0.1
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 +2 -1
- package/README.md +2 -1
- package/dist/hooks/think-mode/types.d.ts +1 -0
- package/dist/hooks/ultrawork-mode/index.d.ts +2 -2
- package/dist/index.js +99 -22
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -332,6 +332,7 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
|
|
332
332
|
- Use camelCase for function names
|
|
333
333
|
```
|
|
334
334
|
- **Think Mode**: 확장된 사고(Extended Thinking)가 필요한 상황을 자동으로 감지하고 모드를 전환합니다. 사용자가 깊은 사고를 요청하는 표현(예: "think deeply", "ultrathink")을 감지하면, 추론 능력을 극대화하도록 모델 설정을 동적으로 조정합니다.
|
|
335
|
+
- **Ultrawork Mode**: 사용자가 "ultrawork" 또는 "ulw" 키워드를 입력하면 자동으로 에이전트 오케스트레이션 가이드를 주입합니다. 메인 에이전트가 모든 가용한 전문 에이전트(탐색, 사서, 계획, UI)를 백그라운드 작업을 통해 병렬로 최대한 활용하도록 강제하며, 엄격한 TODO 추적 및 검증 프로토콜을 따르게 합니다.
|
|
335
336
|
- **Anthropic Auto Compact**: Anthropic 모델 사용 시 컨텍스트 한계에 도달하면 대화 기록을 자동으로 압축하여 효율적으로 관리합니다.
|
|
336
337
|
- **Empty Task Response Detector**: 서브 에이전트가 수행한 작업이 비어있거나 무의미한 응답을 반환하는 경우를 감지하여, 오류 없이 우아하게 처리합니다.
|
|
337
338
|
- **Grep Output Truncator**: Grep 검색 결과가 너무 길어 컨텍스트를 장악해버리는 것을 방지하기 위해, 과도한 출력을 자동으로 자릅니다.
|
|
@@ -344,7 +345,7 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
|
|
344
345
|
}
|
|
345
346
|
```
|
|
346
347
|
|
|
347
|
-
사용 가능한 훅: `todo-continuation-enforcer`, `context-window-monitor`, `session-recovery`, `session-notification`, `comment-checker`, `grep-output-truncator`, `directory-agents-injector`, `directory-readme-injector`, `empty-task-response-detector`, `think-mode`, `anthropic-auto-compact`, `rules-injector`, `background-notification`, `auto-update-checker`
|
|
348
|
+
사용 가능한 훅: `todo-continuation-enforcer`, `context-window-monitor`, `session-recovery`, `session-notification`, `comment-checker`, `grep-output-truncator`, `directory-agents-injector`, `directory-readme-injector`, `empty-task-response-detector`, `think-mode`, `ultrawork-mode`, `anthropic-auto-compact`, `rules-injector`, `background-notification`, `auto-update-checker`
|
|
348
349
|
|
|
349
350
|
> **참고**: `disabled_hooks`는 Oh My OpenCode의 내장 훅을 제어합니다. Claude Code의 `settings.json` 훅을 비활성화하려면 `claude_code.hooks: false`를 대신 사용하세요 ([호환성 토글](#호환성-토글) 참고).
|
|
350
351
|
|
package/README.md
CHANGED
|
@@ -330,6 +330,7 @@ Example workflow:
|
|
|
330
330
|
- Use camelCase for function names
|
|
331
331
|
```
|
|
332
332
|
- **Think Mode**: Automatic extended thinking detection and mode switching. Detects when user requests deep thinking (e.g., "think deeply", "ultrathink") and dynamically adjusts model settings for enhanced reasoning.
|
|
333
|
+
- **Ultrawork Mode**: When user triggers "ultrawork" or "ulw" keywords, automatically injects agent orchestration guidance. Forces the main agent to leverage all available specialized agents (exploration, librarian, planning, UI) via background tasks in parallel, with strict TODO tracking and verification protocols.
|
|
333
334
|
- **Anthropic Auto Compact**: Automatically compacts conversation history when approaching context limits for Anthropic models.
|
|
334
335
|
- **Empty Task Response Detector**: Detects when subagent tasks return empty or meaningless responses and handles gracefully.
|
|
335
336
|
- **Grep Output Truncator**: Prevents grep output from overwhelming the context by truncating excessively long results.
|
|
@@ -342,7 +343,7 @@ You can disable specific built-in hooks using `disabled_hooks` in `~/.config/ope
|
|
|
342
343
|
}
|
|
343
344
|
```
|
|
344
345
|
|
|
345
|
-
Available hooks: `todo-continuation-enforcer`, `context-window-monitor`, `session-recovery`, `session-notification`, `comment-checker`, `grep-output-truncator`, `directory-agents-injector`, `directory-readme-injector`, `empty-task-response-detector`, `think-mode`, `anthropic-auto-compact`, `rules-injector`, `background-notification`, `auto-update-checker`
|
|
346
|
+
Available hooks: `todo-continuation-enforcer`, `context-window-monitor`, `session-recovery`, `session-notification`, `comment-checker`, `grep-output-truncator`, `directory-agents-injector`, `directory-readme-injector`, `empty-task-response-detector`, `think-mode`, `ultrawork-mode`, `anthropic-auto-compact`, `rules-injector`, `background-notification`, `auto-update-checker`
|
|
346
347
|
|
|
347
348
|
> **Note**: `disabled_hooks` controls Oh My OpenCode's built-in hooks. To disable Claude Code's `settings.json` hooks, use `claude_code.hooks: false` instead (see [Compatibility Toggles](#compatibility-toggles)).
|
|
348
349
|
|
|
@@ -4,13 +4,13 @@ export * from "./types";
|
|
|
4
4
|
export declare function clearUltraworkModeState(sessionID: string): void;
|
|
5
5
|
export declare function createUltraworkModeHook(): {
|
|
6
6
|
/**
|
|
7
|
-
* chat.message hook - detect ultrawork/ulw keywords, inject context
|
|
7
|
+
* chat.message hook - detect ultrawork/ulw keywords, inject context via history
|
|
8
8
|
*
|
|
9
9
|
* Execution timing: AFTER claudeCodeHooks["chat.message"]
|
|
10
10
|
* Behavior:
|
|
11
11
|
* 1. Extract text from user prompt
|
|
12
12
|
* 2. Detect ultrawork/ulw keywords (excluding code blocks)
|
|
13
|
-
* 3. If detected,
|
|
13
|
+
* 3. If detected, inject ULTRAWORK_CONTEXT via injectHookMessage (history injection)
|
|
14
14
|
*/
|
|
15
15
|
"chat.message": (input: {
|
|
16
16
|
sessionID: string;
|
package/dist/index.js
CHANGED
|
@@ -4817,6 +4817,46 @@ var ALREADY_HIGH = new Set([
|
|
|
4817
4817
|
"gpt-5.2-chat-latest-high",
|
|
4818
4818
|
"gpt-5.2-pro-high"
|
|
4819
4819
|
]);
|
|
4820
|
+
var THINKING_CONFIGS = {
|
|
4821
|
+
anthropic: {
|
|
4822
|
+
thinking: {
|
|
4823
|
+
type: "enabled",
|
|
4824
|
+
budgetTokens: 64000
|
|
4825
|
+
},
|
|
4826
|
+
maxTokens: 128000
|
|
4827
|
+
},
|
|
4828
|
+
"amazon-bedrock": {
|
|
4829
|
+
reasoningConfig: {
|
|
4830
|
+
type: "enabled",
|
|
4831
|
+
budgetTokens: 32000
|
|
4832
|
+
},
|
|
4833
|
+
maxTokens: 64000
|
|
4834
|
+
},
|
|
4835
|
+
google: {
|
|
4836
|
+
providerOptions: {
|
|
4837
|
+
google: {
|
|
4838
|
+
thinkingConfig: {
|
|
4839
|
+
thinkingLevel: "HIGH"
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
},
|
|
4844
|
+
"google-vertex": {
|
|
4845
|
+
providerOptions: {
|
|
4846
|
+
"google-vertex": {
|
|
4847
|
+
thinkingConfig: {
|
|
4848
|
+
thinkingLevel: "HIGH"
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4853
|
+
};
|
|
4854
|
+
var THINKING_CAPABLE_MODELS = {
|
|
4855
|
+
anthropic: ["claude-sonnet-4", "claude-opus-4", "claude-3"],
|
|
4856
|
+
"amazon-bedrock": ["claude", "anthropic"],
|
|
4857
|
+
google: ["gemini-2", "gemini-3"],
|
|
4858
|
+
"google-vertex": ["gemini-2", "gemini-3"]
|
|
4859
|
+
};
|
|
4820
4860
|
function getHighVariant(modelID) {
|
|
4821
4861
|
if (ALREADY_HIGH.has(modelID)) {
|
|
4822
4862
|
return null;
|
|
@@ -4826,6 +4866,19 @@ function getHighVariant(modelID) {
|
|
|
4826
4866
|
function isAlreadyHighVariant(modelID) {
|
|
4827
4867
|
return ALREADY_HIGH.has(modelID) || modelID.endsWith("-high");
|
|
4828
4868
|
}
|
|
4869
|
+
function getThinkingConfig(providerID, modelID) {
|
|
4870
|
+
if (isAlreadyHighVariant(modelID)) {
|
|
4871
|
+
return null;
|
|
4872
|
+
}
|
|
4873
|
+
const config = THINKING_CONFIGS[providerID];
|
|
4874
|
+
const capablePatterns = THINKING_CAPABLE_MODELS[providerID];
|
|
4875
|
+
if (!config || !capablePatterns) {
|
|
4876
|
+
return null;
|
|
4877
|
+
}
|
|
4878
|
+
const modelLower = modelID.toLowerCase();
|
|
4879
|
+
const isCapable = capablePatterns.some((pattern) => modelLower.includes(pattern.toLowerCase()));
|
|
4880
|
+
return isCapable ? config : null;
|
|
4881
|
+
}
|
|
4829
4882
|
|
|
4830
4883
|
// src/hooks/think-mode/index.ts
|
|
4831
4884
|
var thinkModeState = new Map;
|
|
@@ -4835,7 +4888,8 @@ function createThinkModeHook() {
|
|
|
4835
4888
|
const promptText = extractPromptText(output.parts);
|
|
4836
4889
|
const state = {
|
|
4837
4890
|
requested: false,
|
|
4838
|
-
modelSwitched: false
|
|
4891
|
+
modelSwitched: false,
|
|
4892
|
+
thinkingConfigInjected: false
|
|
4839
4893
|
};
|
|
4840
4894
|
if (!detectThinkKeyword(promptText)) {
|
|
4841
4895
|
thinkModeState.set(sessionID, state);
|
|
@@ -4854,15 +4908,28 @@ function createThinkModeHook() {
|
|
|
4854
4908
|
return;
|
|
4855
4909
|
}
|
|
4856
4910
|
const highVariant = getHighVariant(currentModel.modelID);
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4911
|
+
const thinkingConfig = getThinkingConfig(currentModel.providerID, currentModel.modelID);
|
|
4912
|
+
if (highVariant) {
|
|
4913
|
+
output.message.model = {
|
|
4914
|
+
providerID: currentModel.providerID,
|
|
4915
|
+
modelID: highVariant
|
|
4916
|
+
};
|
|
4917
|
+
state.modelSwitched = true;
|
|
4918
|
+
log("Think mode: model switched to high variant", {
|
|
4919
|
+
sessionID,
|
|
4920
|
+
from: currentModel.modelID,
|
|
4921
|
+
to: highVariant
|
|
4922
|
+
});
|
|
4923
|
+
}
|
|
4924
|
+
if (thinkingConfig) {
|
|
4925
|
+
Object.assign(output.message, thinkingConfig);
|
|
4926
|
+
state.thinkingConfigInjected = true;
|
|
4927
|
+
log("Think mode: thinking config injected", {
|
|
4928
|
+
sessionID,
|
|
4929
|
+
provider: currentModel.providerID,
|
|
4930
|
+
config: thinkingConfig
|
|
4931
|
+
});
|
|
4860
4932
|
}
|
|
4861
|
-
output.message.model = {
|
|
4862
|
-
providerID: currentModel.providerID,
|
|
4863
|
-
modelID: highVariant
|
|
4864
|
-
};
|
|
4865
|
-
state.modelSwitched = true;
|
|
4866
4933
|
thinkModeState.set(sessionID, state);
|
|
4867
4934
|
},
|
|
4868
4935
|
event: async ({ event }) => {
|
|
@@ -6586,12 +6653,18 @@ function createUltraworkModeHook() {
|
|
|
6586
6653
|
}
|
|
6587
6654
|
state.detected = true;
|
|
6588
6655
|
log("Ultrawork keyword detected", { sessionID: input.sessionID });
|
|
6589
|
-
const
|
|
6590
|
-
const
|
|
6591
|
-
|
|
6592
|
-
|
|
6656
|
+
const message = output.message;
|
|
6657
|
+
const success = injectHookMessage(input.sessionID, ULTRAWORK_CONTEXT, {
|
|
6658
|
+
agent: message.agent,
|
|
6659
|
+
model: message.model,
|
|
6660
|
+
path: message.path,
|
|
6661
|
+
tools: message.tools
|
|
6662
|
+
});
|
|
6663
|
+
if (success) {
|
|
6593
6664
|
state.injected = true;
|
|
6594
|
-
log("Ultrawork context injected", { sessionID: input.sessionID });
|
|
6665
|
+
log("Ultrawork context injected via history", { sessionID: input.sessionID });
|
|
6666
|
+
} else {
|
|
6667
|
+
log("Ultrawork context injection failed", { sessionID: input.sessionID });
|
|
6595
6668
|
}
|
|
6596
6669
|
ultraworkModeState.set(input.sessionID, state);
|
|
6597
6670
|
},
|
|
@@ -8190,7 +8263,7 @@ function loadOpencodeProjectCommands() {
|
|
|
8190
8263
|
return commandsToRecord(commands);
|
|
8191
8264
|
}
|
|
8192
8265
|
// src/features/claude-code-skill-loader/loader.ts
|
|
8193
|
-
import { existsSync as existsSync20, readdirSync as readdirSync5, readFileSync as readFileSync12,
|
|
8266
|
+
import { existsSync as existsSync20, readdirSync as readdirSync5, readFileSync as readFileSync12, lstatSync, readlinkSync } from "fs";
|
|
8194
8267
|
import { homedir as homedir10 } from "os";
|
|
8195
8268
|
import { join as join25, resolve as resolve4 } from "path";
|
|
8196
8269
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
@@ -8206,8 +8279,12 @@ function loadSkillsFromDir(skillsDir, scope) {
|
|
|
8206
8279
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
8207
8280
|
continue;
|
|
8208
8281
|
let resolvedPath = skillPath;
|
|
8209
|
-
|
|
8210
|
-
|
|
8282
|
+
try {
|
|
8283
|
+
if (lstatSync(skillPath, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
8284
|
+
resolvedPath = resolve4(skillPath, "..", readlinkSync(skillPath));
|
|
8285
|
+
}
|
|
8286
|
+
} catch {
|
|
8287
|
+
continue;
|
|
8211
8288
|
}
|
|
8212
8289
|
const skillMdPath = join25(resolvedPath, "SKILL.md");
|
|
8213
8290
|
if (!existsSync20(skillMdPath))
|
|
@@ -22308,7 +22385,7 @@ var lsp_code_action_resolve = tool({
|
|
|
22308
22385
|
// src/tools/ast-grep/constants.ts
|
|
22309
22386
|
import { createRequire as createRequire4 } from "module";
|
|
22310
22387
|
import { dirname as dirname5, join as join30 } from "path";
|
|
22311
|
-
import { existsSync as existsSync26, statSync as
|
|
22388
|
+
import { existsSync as existsSync26, statSync as statSync3 } from "fs";
|
|
22312
22389
|
|
|
22313
22390
|
// src/tools/ast-grep/downloader.ts
|
|
22314
22391
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
@@ -22422,7 +22499,7 @@ async function ensureAstGrepBinary() {
|
|
|
22422
22499
|
// src/tools/ast-grep/constants.ts
|
|
22423
22500
|
function isValidBinary(filePath) {
|
|
22424
22501
|
try {
|
|
22425
|
-
return
|
|
22502
|
+
return statSync3(filePath).size > 1e4;
|
|
22426
22503
|
} catch {
|
|
22427
22504
|
return false;
|
|
22428
22505
|
}
|
|
@@ -23442,7 +23519,7 @@ var SkillFrontmatterSchema = exports_external.object({
|
|
|
23442
23519
|
metadata: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
23443
23520
|
});
|
|
23444
23521
|
// src/tools/skill/tools.ts
|
|
23445
|
-
import { existsSync as existsSync30, readdirSync as readdirSync8,
|
|
23522
|
+
import { existsSync as existsSync30, readdirSync as readdirSync8, lstatSync as lstatSync2, readlinkSync as readlinkSync2, readFileSync as readFileSync18 } from "fs";
|
|
23446
23523
|
import { homedir as homedir16 } from "os";
|
|
23447
23524
|
import { join as join33, resolve as resolve7, basename as basename4 } from "path";
|
|
23448
23525
|
function parseSkillFrontmatter(data) {
|
|
@@ -23467,7 +23544,7 @@ function discoverSkillsFromDir(skillsDir, scope) {
|
|
|
23467
23544
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
23468
23545
|
let resolvedPath = skillPath;
|
|
23469
23546
|
try {
|
|
23470
|
-
const stats =
|
|
23547
|
+
const stats = lstatSync2(skillPath, { throwIfNoEntry: false });
|
|
23471
23548
|
if (stats?.isSymbolicLink()) {
|
|
23472
23549
|
resolvedPath = resolve7(skillPath, "..", readlinkSync2(skillPath));
|
|
23473
23550
|
}
|
|
@@ -23504,7 +23581,7 @@ var skillListForDescription = availableSkills.map((s) => `- ${s.name}: ${s.descr
|
|
|
23504
23581
|
`);
|
|
23505
23582
|
function resolveSymlink(skillPath) {
|
|
23506
23583
|
try {
|
|
23507
|
-
const stats =
|
|
23584
|
+
const stats = lstatSync2(skillPath, { throwIfNoEntry: false });
|
|
23508
23585
|
if (stats?.isSymbolicLink()) {
|
|
23509
23586
|
return resolve7(skillPath, "..", readlinkSync2(skillPath));
|
|
23510
23587
|
}
|