oh-my-opencode 1.0.0 → 1.0.2
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 +159 -103
- package/dist/shared/deep-merge.d.ts +1 -0
- package/dist/shared/file-utils.d.ts +6 -0
- package/dist/shared/index.d.ts +1 -0
- 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
|
@@ -2586,13 +2586,42 @@ function log(message, data) {
|
|
|
2586
2586
|
fs.appendFileSync(logFile, logEntry);
|
|
2587
2587
|
} catch {}
|
|
2588
2588
|
}
|
|
2589
|
+
// src/shared/deep-merge.ts
|
|
2590
|
+
var DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
2591
|
+
var MAX_DEPTH = 50;
|
|
2592
|
+
function isPlainObject(value) {
|
|
2593
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
|
|
2594
|
+
}
|
|
2595
|
+
function deepMerge(base, override, depth = 0) {
|
|
2596
|
+
if (!base && !override)
|
|
2597
|
+
return;
|
|
2598
|
+
if (!base)
|
|
2599
|
+
return override;
|
|
2600
|
+
if (!override)
|
|
2601
|
+
return base;
|
|
2602
|
+
if (depth > MAX_DEPTH)
|
|
2603
|
+
return override ?? base;
|
|
2604
|
+
const result = { ...base };
|
|
2605
|
+
for (const key of Object.keys(override)) {
|
|
2606
|
+
if (DANGEROUS_KEYS.has(key))
|
|
2607
|
+
continue;
|
|
2608
|
+
const baseValue = base[key];
|
|
2609
|
+
const overrideValue = override[key];
|
|
2610
|
+
if (overrideValue === undefined)
|
|
2611
|
+
continue;
|
|
2612
|
+
if (isPlainObject(baseValue) && isPlainObject(overrideValue)) {
|
|
2613
|
+
result[key] = deepMerge(baseValue, overrideValue, depth + 1);
|
|
2614
|
+
} else {
|
|
2615
|
+
result[key] = overrideValue;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
return result;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2589
2621
|
// src/shared/snake-case.ts
|
|
2590
2622
|
function camelToSnake(str) {
|
|
2591
2623
|
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
2592
2624
|
}
|
|
2593
|
-
function isPlainObject(value) {
|
|
2594
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2595
|
-
}
|
|
2596
2625
|
function objectToSnakeCase(obj, deep = true) {
|
|
2597
2626
|
const result = {};
|
|
2598
2627
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -2665,36 +2694,22 @@ function isHookDisabled(config, hookType) {
|
|
|
2665
2694
|
}
|
|
2666
2695
|
return false;
|
|
2667
2696
|
}
|
|
2668
|
-
// src/shared/
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
function
|
|
2672
|
-
return
|
|
2697
|
+
// src/shared/file-utils.ts
|
|
2698
|
+
import { lstatSync, readlinkSync } from "fs";
|
|
2699
|
+
import { resolve } from "path";
|
|
2700
|
+
function isMarkdownFile(entry) {
|
|
2701
|
+
return !entry.name.startsWith(".") && entry.name.endsWith(".md") && entry.isFile();
|
|
2673
2702
|
}
|
|
2674
|
-
function
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
if (!override)
|
|
2680
|
-
return base;
|
|
2681
|
-
if (depth > MAX_DEPTH)
|
|
2682
|
-
return override ?? base;
|
|
2683
|
-
const result = { ...base };
|
|
2684
|
-
for (const key of Object.keys(override)) {
|
|
2685
|
-
if (DANGEROUS_KEYS.has(key))
|
|
2686
|
-
continue;
|
|
2687
|
-
const baseValue = base[key];
|
|
2688
|
-
const overrideValue = override[key];
|
|
2689
|
-
if (overrideValue === undefined)
|
|
2690
|
-
continue;
|
|
2691
|
-
if (isPlainObject2(baseValue) && isPlainObject2(overrideValue)) {
|
|
2692
|
-
result[key] = deepMerge(baseValue, overrideValue, depth + 1);
|
|
2693
|
-
} else {
|
|
2694
|
-
result[key] = overrideValue;
|
|
2703
|
+
function resolveSymlink(filePath) {
|
|
2704
|
+
try {
|
|
2705
|
+
const stats = lstatSync(filePath, { throwIfNoEntry: false });
|
|
2706
|
+
if (stats?.isSymbolicLink()) {
|
|
2707
|
+
return resolve(filePath, "..", readlinkSync(filePath));
|
|
2695
2708
|
}
|
|
2709
|
+
return filePath;
|
|
2710
|
+
} catch {
|
|
2711
|
+
return filePath;
|
|
2696
2712
|
}
|
|
2697
|
-
return result;
|
|
2698
2713
|
}
|
|
2699
2714
|
// src/agents/utils.ts
|
|
2700
2715
|
var allBuiltinAgents = {
|
|
@@ -4042,7 +4057,7 @@ function createGrepOutputTruncatorHook(ctx) {
|
|
|
4042
4057
|
}
|
|
4043
4058
|
// src/hooks/directory-agents-injector/index.ts
|
|
4044
4059
|
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
|
|
4045
|
-
import { dirname as dirname2, join as join9, resolve } from "path";
|
|
4060
|
+
import { dirname as dirname2, join as join9, resolve as resolve2 } from "path";
|
|
4046
4061
|
|
|
4047
4062
|
// src/hooks/directory-agents-injector/storage.ts
|
|
4048
4063
|
import {
|
|
@@ -4108,7 +4123,7 @@ function createDirectoryAgentsInjectorHook(ctx) {
|
|
|
4108
4123
|
return null;
|
|
4109
4124
|
if (title.startsWith("/"))
|
|
4110
4125
|
return title;
|
|
4111
|
-
return
|
|
4126
|
+
return resolve2(ctx.directory, title);
|
|
4112
4127
|
}
|
|
4113
4128
|
function findAgentsMdUp(startDir) {
|
|
4114
4129
|
const found = [];
|
|
@@ -4183,7 +4198,7 @@ ${content}`;
|
|
|
4183
4198
|
}
|
|
4184
4199
|
// src/hooks/directory-readme-injector/index.ts
|
|
4185
4200
|
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
4186
|
-
import { dirname as dirname3, join as join12, resolve as
|
|
4201
|
+
import { dirname as dirname3, join as join12, resolve as resolve3 } from "path";
|
|
4187
4202
|
|
|
4188
4203
|
// src/hooks/directory-readme-injector/storage.ts
|
|
4189
4204
|
import {
|
|
@@ -4249,7 +4264,7 @@ function createDirectoryReadmeInjectorHook(ctx) {
|
|
|
4249
4264
|
return null;
|
|
4250
4265
|
if (title.startsWith("/"))
|
|
4251
4266
|
return title;
|
|
4252
|
-
return
|
|
4267
|
+
return resolve3(ctx.directory, title);
|
|
4253
4268
|
}
|
|
4254
4269
|
function findReadmeMdUp(startDir) {
|
|
4255
4270
|
const found = [];
|
|
@@ -4817,6 +4832,46 @@ var ALREADY_HIGH = new Set([
|
|
|
4817
4832
|
"gpt-5.2-chat-latest-high",
|
|
4818
4833
|
"gpt-5.2-pro-high"
|
|
4819
4834
|
]);
|
|
4835
|
+
var THINKING_CONFIGS = {
|
|
4836
|
+
anthropic: {
|
|
4837
|
+
thinking: {
|
|
4838
|
+
type: "enabled",
|
|
4839
|
+
budgetTokens: 64000
|
|
4840
|
+
},
|
|
4841
|
+
maxTokens: 128000
|
|
4842
|
+
},
|
|
4843
|
+
"amazon-bedrock": {
|
|
4844
|
+
reasoningConfig: {
|
|
4845
|
+
type: "enabled",
|
|
4846
|
+
budgetTokens: 32000
|
|
4847
|
+
},
|
|
4848
|
+
maxTokens: 64000
|
|
4849
|
+
},
|
|
4850
|
+
google: {
|
|
4851
|
+
providerOptions: {
|
|
4852
|
+
google: {
|
|
4853
|
+
thinkingConfig: {
|
|
4854
|
+
thinkingLevel: "HIGH"
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
},
|
|
4859
|
+
"google-vertex": {
|
|
4860
|
+
providerOptions: {
|
|
4861
|
+
"google-vertex": {
|
|
4862
|
+
thinkingConfig: {
|
|
4863
|
+
thinkingLevel: "HIGH"
|
|
4864
|
+
}
|
|
4865
|
+
}
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4868
|
+
};
|
|
4869
|
+
var THINKING_CAPABLE_MODELS = {
|
|
4870
|
+
anthropic: ["claude-sonnet-4", "claude-opus-4", "claude-3"],
|
|
4871
|
+
"amazon-bedrock": ["claude", "anthropic"],
|
|
4872
|
+
google: ["gemini-2", "gemini-3"],
|
|
4873
|
+
"google-vertex": ["gemini-2", "gemini-3"]
|
|
4874
|
+
};
|
|
4820
4875
|
function getHighVariant(modelID) {
|
|
4821
4876
|
if (ALREADY_HIGH.has(modelID)) {
|
|
4822
4877
|
return null;
|
|
@@ -4826,6 +4881,19 @@ function getHighVariant(modelID) {
|
|
|
4826
4881
|
function isAlreadyHighVariant(modelID) {
|
|
4827
4882
|
return ALREADY_HIGH.has(modelID) || modelID.endsWith("-high");
|
|
4828
4883
|
}
|
|
4884
|
+
function getThinkingConfig(providerID, modelID) {
|
|
4885
|
+
if (isAlreadyHighVariant(modelID)) {
|
|
4886
|
+
return null;
|
|
4887
|
+
}
|
|
4888
|
+
const config = THINKING_CONFIGS[providerID];
|
|
4889
|
+
const capablePatterns = THINKING_CAPABLE_MODELS[providerID];
|
|
4890
|
+
if (!config || !capablePatterns) {
|
|
4891
|
+
return null;
|
|
4892
|
+
}
|
|
4893
|
+
const modelLower = modelID.toLowerCase();
|
|
4894
|
+
const isCapable = capablePatterns.some((pattern) => modelLower.includes(pattern.toLowerCase()));
|
|
4895
|
+
return isCapable ? config : null;
|
|
4896
|
+
}
|
|
4829
4897
|
|
|
4830
4898
|
// src/hooks/think-mode/index.ts
|
|
4831
4899
|
var thinkModeState = new Map;
|
|
@@ -4835,7 +4903,8 @@ function createThinkModeHook() {
|
|
|
4835
4903
|
const promptText = extractPromptText(output.parts);
|
|
4836
4904
|
const state = {
|
|
4837
4905
|
requested: false,
|
|
4838
|
-
modelSwitched: false
|
|
4906
|
+
modelSwitched: false,
|
|
4907
|
+
thinkingConfigInjected: false
|
|
4839
4908
|
};
|
|
4840
4909
|
if (!detectThinkKeyword(promptText)) {
|
|
4841
4910
|
thinkModeState.set(sessionID, state);
|
|
@@ -4854,15 +4923,28 @@ function createThinkModeHook() {
|
|
|
4854
4923
|
return;
|
|
4855
4924
|
}
|
|
4856
4925
|
const highVariant = getHighVariant(currentModel.modelID);
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4926
|
+
const thinkingConfig = getThinkingConfig(currentModel.providerID, currentModel.modelID);
|
|
4927
|
+
if (highVariant) {
|
|
4928
|
+
output.message.model = {
|
|
4929
|
+
providerID: currentModel.providerID,
|
|
4930
|
+
modelID: highVariant
|
|
4931
|
+
};
|
|
4932
|
+
state.modelSwitched = true;
|
|
4933
|
+
log("Think mode: model switched to high variant", {
|
|
4934
|
+
sessionID,
|
|
4935
|
+
from: currentModel.modelID,
|
|
4936
|
+
to: highVariant
|
|
4937
|
+
});
|
|
4938
|
+
}
|
|
4939
|
+
if (thinkingConfig) {
|
|
4940
|
+
Object.assign(output.message, thinkingConfig);
|
|
4941
|
+
state.thinkingConfigInjected = true;
|
|
4942
|
+
log("Think mode: thinking config injected", {
|
|
4943
|
+
sessionID,
|
|
4944
|
+
provider: currentModel.providerID,
|
|
4945
|
+
config: thinkingConfig
|
|
4946
|
+
});
|
|
4860
4947
|
}
|
|
4861
|
-
output.message.model = {
|
|
4862
|
-
providerID: currentModel.providerID,
|
|
4863
|
-
modelID: highVariant
|
|
4864
|
-
};
|
|
4865
|
-
state.modelSwitched = true;
|
|
4866
4948
|
thinkModeState.set(sessionID, state);
|
|
4867
4949
|
},
|
|
4868
4950
|
event: async ({ event }) => {
|
|
@@ -5907,7 +5989,7 @@ ${result.message}`;
|
|
|
5907
5989
|
// src/hooks/rules-injector/index.ts
|
|
5908
5990
|
import { readFileSync as readFileSync9 } from "fs";
|
|
5909
5991
|
import { homedir as homedir7 } from "os";
|
|
5910
|
-
import { relative as relative3, resolve as
|
|
5992
|
+
import { relative as relative3, resolve as resolve4 } from "path";
|
|
5911
5993
|
|
|
5912
5994
|
// src/hooks/rules-injector/finder.ts
|
|
5913
5995
|
import {
|
|
@@ -6264,7 +6346,7 @@ function createRulesInjectorHook(ctx) {
|
|
|
6264
6346
|
return null;
|
|
6265
6347
|
if (title.startsWith("/"))
|
|
6266
6348
|
return title;
|
|
6267
|
-
return
|
|
6349
|
+
return resolve4(ctx.directory, title);
|
|
6268
6350
|
}
|
|
6269
6351
|
const toolExecuteAfter = async (input, output) => {
|
|
6270
6352
|
if (!TRACKED_TOOLS.includes(input.tool.toLowerCase()))
|
|
@@ -6586,12 +6668,18 @@ function createUltraworkModeHook() {
|
|
|
6586
6668
|
}
|
|
6587
6669
|
state.detected = true;
|
|
6588
6670
|
log("Ultrawork keyword detected", { sessionID: input.sessionID });
|
|
6589
|
-
const
|
|
6590
|
-
const
|
|
6591
|
-
|
|
6592
|
-
|
|
6671
|
+
const message = output.message;
|
|
6672
|
+
const success = injectHookMessage(input.sessionID, ULTRAWORK_CONTEXT, {
|
|
6673
|
+
agent: message.agent,
|
|
6674
|
+
model: message.model,
|
|
6675
|
+
path: message.path,
|
|
6676
|
+
tools: message.tools
|
|
6677
|
+
});
|
|
6678
|
+
if (success) {
|
|
6593
6679
|
state.injected = true;
|
|
6594
|
-
log("Ultrawork context injected", { sessionID: input.sessionID });
|
|
6680
|
+
log("Ultrawork context injected via history", { sessionID: input.sessionID });
|
|
6681
|
+
} else {
|
|
6682
|
+
log("Ultrawork context injection failed", { sessionID: input.sessionID });
|
|
6595
6683
|
}
|
|
6596
6684
|
ultraworkModeState.set(input.sessionID, state);
|
|
6597
6685
|
},
|
|
@@ -6854,8 +6942,8 @@ function startCallbackServer(timeoutMs = 5 * 60 * 1000) {
|
|
|
6854
6942
|
});
|
|
6855
6943
|
const actualPort = server.port;
|
|
6856
6944
|
const waitForCallback = () => {
|
|
6857
|
-
return new Promise((
|
|
6858
|
-
resolveCallback =
|
|
6945
|
+
return new Promise((resolve5, reject) => {
|
|
6946
|
+
resolveCallback = resolve5;
|
|
6859
6947
|
rejectCallback = reject;
|
|
6860
6948
|
timeoutId = setTimeout(() => {
|
|
6861
6949
|
cleanup();
|
|
@@ -7801,7 +7889,7 @@ async function attemptFetch(options) {
|
|
|
7801
7889
|
if (attempt < maxPermissionRetries) {
|
|
7802
7890
|
const delay = calculateRetryDelay2(attempt);
|
|
7803
7891
|
debugLog6(`[RETRY] GCP permission error, retry ${attempt + 1}/${maxPermissionRetries} after ${delay}ms`);
|
|
7804
|
-
await new Promise((
|
|
7892
|
+
await new Promise((resolve5) => setTimeout(resolve5, delay));
|
|
7805
7893
|
continue;
|
|
7806
7894
|
}
|
|
7807
7895
|
debugLog6(`[RETRY] GCP permission error, max retries exceeded`);
|
|
@@ -8116,9 +8204,6 @@ async function createGoogleAntigravityAuthPlugin({
|
|
|
8116
8204
|
import { existsSync as existsSync19, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "fs";
|
|
8117
8205
|
import { homedir as homedir9 } from "os";
|
|
8118
8206
|
import { join as join24, basename } from "path";
|
|
8119
|
-
function isMarkdownFile(entry) {
|
|
8120
|
-
return !entry.name.startsWith(".") && entry.name.endsWith(".md") && entry.isFile();
|
|
8121
|
-
}
|
|
8122
8207
|
function loadCommandsFromDir(commandsDir, scope) {
|
|
8123
8208
|
if (!existsSync19(commandsDir)) {
|
|
8124
8209
|
return [];
|
|
@@ -8190,9 +8275,9 @@ function loadOpencodeProjectCommands() {
|
|
|
8190
8275
|
return commandsToRecord(commands);
|
|
8191
8276
|
}
|
|
8192
8277
|
// src/features/claude-code-skill-loader/loader.ts
|
|
8193
|
-
import { existsSync as existsSync20, readdirSync as readdirSync5, readFileSync as readFileSync12
|
|
8278
|
+
import { existsSync as existsSync20, readdirSync as readdirSync5, readFileSync as readFileSync12 } from "fs";
|
|
8194
8279
|
import { homedir as homedir10 } from "os";
|
|
8195
|
-
import { join as join25
|
|
8280
|
+
import { join as join25 } from "path";
|
|
8196
8281
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
8197
8282
|
if (!existsSync20(skillsDir)) {
|
|
8198
8283
|
return [];
|
|
@@ -8205,10 +8290,7 @@ function loadSkillsFromDir(skillsDir, scope) {
|
|
|
8205
8290
|
const skillPath = join25(skillsDir, entry.name);
|
|
8206
8291
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
8207
8292
|
continue;
|
|
8208
|
-
|
|
8209
|
-
if (statSync3(skillPath, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
8210
|
-
resolvedPath = resolve4(skillPath, "..", readlinkSync(skillPath));
|
|
8211
|
-
}
|
|
8293
|
+
const resolvedPath = resolveSymlink(skillPath);
|
|
8212
8294
|
const skillMdPath = join25(resolvedPath, "SKILL.md");
|
|
8213
8295
|
if (!existsSync20(skillMdPath))
|
|
8214
8296
|
continue;
|
|
@@ -8275,9 +8357,6 @@ function parseToolsConfig(toolsStr) {
|
|
|
8275
8357
|
}
|
|
8276
8358
|
return result;
|
|
8277
8359
|
}
|
|
8278
|
-
function isMarkdownFile2(entry) {
|
|
8279
|
-
return !entry.name.startsWith(".") && entry.name.endsWith(".md") && entry.isFile();
|
|
8280
|
-
}
|
|
8281
8360
|
function loadAgentsFromDir(agentsDir, scope) {
|
|
8282
8361
|
if (!existsSync21(agentsDir)) {
|
|
8283
8362
|
return [];
|
|
@@ -8285,7 +8364,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
8285
8364
|
const entries = readdirSync6(agentsDir, { withFileTypes: true });
|
|
8286
8365
|
const agents = [];
|
|
8287
8366
|
for (const entry of entries) {
|
|
8288
|
-
if (!
|
|
8367
|
+
if (!isMarkdownFile(entry))
|
|
8289
8368
|
continue;
|
|
8290
8369
|
const agentPath = join26(agentsDir, entry.name);
|
|
8291
8370
|
const agentName = basename2(entry.name, ".md");
|
|
@@ -10248,7 +10327,7 @@ __export(exports_util, {
|
|
|
10248
10327
|
jsonStringifyReplacer: () => jsonStringifyReplacer,
|
|
10249
10328
|
joinValues: () => joinValues,
|
|
10250
10329
|
issue: () => issue,
|
|
10251
|
-
isPlainObject: () =>
|
|
10330
|
+
isPlainObject: () => isPlainObject2,
|
|
10252
10331
|
isObject: () => isObject,
|
|
10253
10332
|
hexToUint8Array: () => hexToUint8Array,
|
|
10254
10333
|
getSizableOrigin: () => getSizableOrigin,
|
|
@@ -10430,7 +10509,7 @@ var allowsEval = cached(() => {
|
|
|
10430
10509
|
return false;
|
|
10431
10510
|
}
|
|
10432
10511
|
});
|
|
10433
|
-
function
|
|
10512
|
+
function isPlainObject2(o) {
|
|
10434
10513
|
if (isObject(o) === false)
|
|
10435
10514
|
return false;
|
|
10436
10515
|
const ctor = o.constructor;
|
|
@@ -10445,7 +10524,7 @@ function isPlainObject3(o) {
|
|
|
10445
10524
|
return true;
|
|
10446
10525
|
}
|
|
10447
10526
|
function shallowClone(o) {
|
|
10448
|
-
if (
|
|
10527
|
+
if (isPlainObject2(o))
|
|
10449
10528
|
return { ...o };
|
|
10450
10529
|
if (Array.isArray(o))
|
|
10451
10530
|
return [...o];
|
|
@@ -10628,7 +10707,7 @@ function omit(schema, mask) {
|
|
|
10628
10707
|
return clone(schema, def);
|
|
10629
10708
|
}
|
|
10630
10709
|
function extend(schema, shape) {
|
|
10631
|
-
if (!
|
|
10710
|
+
if (!isPlainObject2(shape)) {
|
|
10632
10711
|
throw new Error("Invalid input to extend: expected a plain object");
|
|
10633
10712
|
}
|
|
10634
10713
|
const checks = schema._zod.def.checks;
|
|
@@ -10647,7 +10726,7 @@ function extend(schema, shape) {
|
|
|
10647
10726
|
return clone(schema, def);
|
|
10648
10727
|
}
|
|
10649
10728
|
function safeExtend(schema, shape) {
|
|
10650
|
-
if (!
|
|
10729
|
+
if (!isPlainObject2(shape)) {
|
|
10651
10730
|
throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
10652
10731
|
}
|
|
10653
10732
|
const def = {
|
|
@@ -12797,7 +12876,7 @@ function mergeValues(a, b) {
|
|
|
12797
12876
|
if (a instanceof Date && b instanceof Date && +a === +b) {
|
|
12798
12877
|
return { valid: true, data: a };
|
|
12799
12878
|
}
|
|
12800
|
-
if (
|
|
12879
|
+
if (isPlainObject2(a) && isPlainObject2(b)) {
|
|
12801
12880
|
const bKeys = Object.keys(b);
|
|
12802
12881
|
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
|
|
12803
12882
|
const newObj = { ...a, ...b };
|
|
@@ -12927,7 +13006,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
|
12927
13006
|
$ZodType.init(inst, def);
|
|
12928
13007
|
inst._zod.parse = (payload, ctx) => {
|
|
12929
13008
|
const input = payload.value;
|
|
12930
|
-
if (!
|
|
13009
|
+
if (!isPlainObject2(input)) {
|
|
12931
13010
|
payload.issues.push({
|
|
12932
13011
|
expected: "record",
|
|
12933
13012
|
code: "invalid_type",
|
|
@@ -22308,7 +22387,7 @@ var lsp_code_action_resolve = tool({
|
|
|
22308
22387
|
// src/tools/ast-grep/constants.ts
|
|
22309
22388
|
import { createRequire as createRequire4 } from "module";
|
|
22310
22389
|
import { dirname as dirname5, join as join30 } from "path";
|
|
22311
|
-
import { existsSync as existsSync26, statSync as
|
|
22390
|
+
import { existsSync as existsSync26, statSync as statSync3 } from "fs";
|
|
22312
22391
|
|
|
22313
22392
|
// src/tools/ast-grep/downloader.ts
|
|
22314
22393
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
@@ -22422,7 +22501,7 @@ async function ensureAstGrepBinary() {
|
|
|
22422
22501
|
// src/tools/ast-grep/constants.ts
|
|
22423
22502
|
function isValidBinary(filePath) {
|
|
22424
22503
|
try {
|
|
22425
|
-
return
|
|
22504
|
+
return statSync3(filePath).size > 1e4;
|
|
22426
22505
|
} catch {
|
|
22427
22506
|
return false;
|
|
22428
22507
|
}
|
|
@@ -23274,11 +23353,7 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
23274
23353
|
const entries = readdirSync7(commandsDir, { withFileTypes: true });
|
|
23275
23354
|
const commands = [];
|
|
23276
23355
|
for (const entry of entries) {
|
|
23277
|
-
if (entry
|
|
23278
|
-
continue;
|
|
23279
|
-
if (!entry.name.endsWith(".md"))
|
|
23280
|
-
continue;
|
|
23281
|
-
if (!entry.isFile())
|
|
23356
|
+
if (!isMarkdownFile(entry))
|
|
23282
23357
|
continue;
|
|
23283
23358
|
const commandPath = join32(commandsDir, entry.name);
|
|
23284
23359
|
const commandName = basename3(entry.name, ".md");
|
|
@@ -23442,9 +23517,9 @@ var SkillFrontmatterSchema = exports_external.object({
|
|
|
23442
23517
|
metadata: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
23443
23518
|
});
|
|
23444
23519
|
// src/tools/skill/tools.ts
|
|
23445
|
-
import { existsSync as existsSync30, readdirSync as readdirSync8,
|
|
23520
|
+
import { existsSync as existsSync30, readdirSync as readdirSync8, readFileSync as readFileSync18 } from "fs";
|
|
23446
23521
|
import { homedir as homedir16 } from "os";
|
|
23447
|
-
import { join as join33,
|
|
23522
|
+
import { join as join33, basename as basename4 } from "path";
|
|
23448
23523
|
function parseSkillFrontmatter(data) {
|
|
23449
23524
|
return {
|
|
23450
23525
|
name: typeof data.name === "string" ? data.name : "",
|
|
@@ -23465,15 +23540,7 @@ function discoverSkillsFromDir(skillsDir, scope) {
|
|
|
23465
23540
|
continue;
|
|
23466
23541
|
const skillPath = join33(skillsDir, entry.name);
|
|
23467
23542
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
23468
|
-
|
|
23469
|
-
try {
|
|
23470
|
-
const stats = statSync5(skillPath, { throwIfNoEntry: false });
|
|
23471
|
-
if (stats?.isSymbolicLink()) {
|
|
23472
|
-
resolvedPath = resolve7(skillPath, "..", readlinkSync2(skillPath));
|
|
23473
|
-
}
|
|
23474
|
-
} catch {
|
|
23475
|
-
continue;
|
|
23476
|
-
}
|
|
23543
|
+
const resolvedPath = resolveSymlink(skillPath);
|
|
23477
23544
|
const skillMdPath = join33(resolvedPath, "SKILL.md");
|
|
23478
23545
|
if (!existsSync30(skillMdPath))
|
|
23479
23546
|
continue;
|
|
@@ -23502,17 +23569,6 @@ function discoverSkillsSync() {
|
|
|
23502
23569
|
var availableSkills = discoverSkillsSync();
|
|
23503
23570
|
var skillListForDescription = availableSkills.map((s) => `- ${s.name}: ${s.description} (${s.scope})`).join(`
|
|
23504
23571
|
`);
|
|
23505
|
-
function resolveSymlink(skillPath) {
|
|
23506
|
-
try {
|
|
23507
|
-
const stats = statSync5(skillPath, { throwIfNoEntry: false });
|
|
23508
|
-
if (stats?.isSymbolicLink()) {
|
|
23509
|
-
return resolve7(skillPath, "..", readlinkSync2(skillPath));
|
|
23510
|
-
}
|
|
23511
|
-
return skillPath;
|
|
23512
|
-
} catch {
|
|
23513
|
-
return skillPath;
|
|
23514
|
-
}
|
|
23515
|
-
}
|
|
23516
23572
|
async function parseSkillMd(skillPath) {
|
|
23517
23573
|
const resolvedPath = resolveSymlink(skillPath);
|
|
23518
23574
|
const skillMdPath = join33(resolvedPath, "SKILL.md");
|
|
@@ -23784,7 +23840,7 @@ Use \`background_output\` tool with task_id="${task.id}" to check progress:
|
|
|
23784
23840
|
});
|
|
23785
23841
|
}
|
|
23786
23842
|
function delay(ms) {
|
|
23787
|
-
return new Promise((
|
|
23843
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
23788
23844
|
}
|
|
23789
23845
|
function truncateText(text, maxLength) {
|
|
23790
23846
|
if (text.length <= maxLength)
|
package/dist/shared/index.d.ts
CHANGED