oh-my-opencode 0.4.4 → 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 +19 -4
- package/README.md +19 -4
- package/dist/config/index.d.ts +2 -2
- package/dist/config/schema.d.ts +35 -0
- package/dist/features/background-agent/types.d.ts +3 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/session-notification.d.ts +2 -0
- package/dist/hooks/think-mode/types.d.ts +1 -0
- package/dist/hooks/ultrawork-mode/constants.d.ts +15 -0
- package/dist/hooks/ultrawork-mode/detector.d.ts +16 -0
- package/dist/hooks/ultrawork-mode/index.d.ts +40 -0
- package/dist/hooks/ultrawork-mode/types.d.ts +20 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +535 -85
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -110,7 +110,7 @@ cat ~/.config/opencode/opencode.json # plugin 배열에 "oh-my-opencode"가 있
|
|
|
110
110
|
|
|
111
111
|
### 4단계: 인증정보 설정
|
|
112
112
|
|
|
113
|
-
plugin 에 oh my opencode 를 추가했다면, tmux 를 사용하여 사용자의 인증 설정을 도와주세요.
|
|
113
|
+
plugin 에 oh my opencode 를 추가했다면, tmux 를 사용하여 사용자의 인증 설정을 도와주세요. **기본적으로 Google Auth (4.2단계) 설정을 권장합니다.**
|
|
114
114
|
|
|
115
115
|
#### 4.1 Anthropic (Claude)
|
|
116
116
|
|
|
@@ -123,7 +123,9 @@ opencode auth login
|
|
|
123
123
|
|
|
124
124
|
#### 4.2 Google Gemini (내장 Antigravity OAuth)
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
**권장되는 기본 설정입니다.**
|
|
127
|
+
|
|
128
|
+
먼저 주도적으로 `~/.config/opencode/oh-my-opencode.json` (또는 프로젝트 레벨의 `.opencode/oh-my-opencode.json`)에서 Google auth를 활성화하세요:
|
|
127
129
|
|
|
128
130
|
```json
|
|
129
131
|
{
|
|
@@ -170,9 +172,9 @@ opencode auth login
|
|
|
170
172
|
|
|
171
173
|
### ⚠️ 주의
|
|
172
174
|
|
|
173
|
-
**사용자가 명시적으로 요청하지 않는 한,
|
|
175
|
+
**사용자가 명시적으로 요청하지 않는 한, 모델 설정을 변경하거나 기능(에이전트, 훅, MCP)을 비활성화하지 마세요.**
|
|
174
176
|
|
|
175
|
-
플러그인은
|
|
177
|
+
플러그인은 기본값으로 완벽하게 동작합니다. 권장 사항인 `google_auth: true` 설정을 제외하고는, 명시적인 요청 없이 다른 설정을 변경하거나 기능을 끄지 마세요.
|
|
176
178
|
|
|
177
179
|
</details>
|
|
178
180
|
|
|
@@ -330,10 +332,23 @@ OpenCode 는 아주 확장가능하고 아주 커스터마이저블합니다.
|
|
|
330
332
|
- Use camelCase for function names
|
|
331
333
|
```
|
|
332
334
|
- **Think Mode**: 확장된 사고(Extended Thinking)가 필요한 상황을 자동으로 감지하고 모드를 전환합니다. 사용자가 깊은 사고를 요청하는 표현(예: "think deeply", "ultrathink")을 감지하면, 추론 능력을 극대화하도록 모델 설정을 동적으로 조정합니다.
|
|
335
|
+
- **Ultrawork Mode**: 사용자가 "ultrawork" 또는 "ulw" 키워드를 입력하면 자동으로 에이전트 오케스트레이션 가이드를 주입합니다. 메인 에이전트가 모든 가용한 전문 에이전트(탐색, 사서, 계획, UI)를 백그라운드 작업을 통해 병렬로 최대한 활용하도록 강제하며, 엄격한 TODO 추적 및 검증 프로토콜을 따르게 합니다.
|
|
333
336
|
- **Anthropic Auto Compact**: Anthropic 모델 사용 시 컨텍스트 한계에 도달하면 대화 기록을 자동으로 압축하여 효율적으로 관리합니다.
|
|
334
337
|
- **Empty Task Response Detector**: 서브 에이전트가 수행한 작업이 비어있거나 무의미한 응답을 반환하는 경우를 감지하여, 오류 없이 우아하게 처리합니다.
|
|
335
338
|
- **Grep Output Truncator**: Grep 검색 결과가 너무 길어 컨텍스트를 장악해버리는 것을 방지하기 위해, 과도한 출력을 자동으로 자릅니다.
|
|
336
339
|
|
|
340
|
+
필요 없는 훅이 있다면, `~/.config/opencode/oh-my-opencode.json` 혹은 `.opencode/oh-my-opencode.json`의 `disabled_hooks`를 사용하여 비활성화할 수 있습니다:
|
|
341
|
+
|
|
342
|
+
```json
|
|
343
|
+
{
|
|
344
|
+
"disabled_hooks": ["session-notification", "comment-checker"]
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
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`
|
|
349
|
+
|
|
350
|
+
> **참고**: `disabled_hooks`는 Oh My OpenCode의 내장 훅을 제어합니다. Claude Code의 `settings.json` 훅을 비활성화하려면 `claude_code.hooks: false`를 대신 사용하세요 ([호환성 토글](#호환성-토글) 참고).
|
|
351
|
+
|
|
337
352
|
### Claude Code 호환성
|
|
338
353
|
|
|
339
354
|
Oh My OpenCode는 Claude Code 설정과 완벽하게 호환됩니다. Claude Code를 사용하셨다면, 기존 설정을 그대로 사용할 수 있습니다.
|
package/README.md
CHANGED
|
@@ -109,7 +109,7 @@ cat ~/.config/opencode/opencode.json # Should contain "oh-my-opencode" in plugi
|
|
|
109
109
|
|
|
110
110
|
### Step 4: Configure Authentication
|
|
111
111
|
|
|
112
|
-
Once `oh-my-opencode` is added to the plugin list, use `tmux` to assist the user with authentication setup.
|
|
112
|
+
Once `oh-my-opencode` is added to the plugin list, use `tmux` to assist the user with authentication setup. **It is RECOMMENDED to set up Google Auth (Step 4.2) by default.**
|
|
113
113
|
|
|
114
114
|
#### 4.1 Anthropic (Claude)
|
|
115
115
|
|
|
@@ -122,7 +122,9 @@ opencode auth login
|
|
|
122
122
|
|
|
123
123
|
#### 4.2 Google Gemini (Built-in Antigravity OAuth)
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
**This is the RECOMMENDED default configuration.**
|
|
126
|
+
|
|
127
|
+
First, proactively enable Google auth in `~/.config/opencode/oh-my-opencode.json` (or `.opencode/oh-my-opencode.json` for project-level):
|
|
126
128
|
|
|
127
129
|
```json
|
|
128
130
|
{
|
|
@@ -169,9 +171,9 @@ For users who prefer proxy-based authentication, [VibeProxy](https://github.com/
|
|
|
169
171
|
|
|
170
172
|
### ⚠️ Warning
|
|
171
173
|
|
|
172
|
-
**Do NOT
|
|
174
|
+
**Do NOT modify model settings or disable features (agents, hooks, MCPs) unless the user explicitly requests it.**
|
|
173
175
|
|
|
174
|
-
The plugin works perfectly with default settings
|
|
176
|
+
The plugin works perfectly with default settings. While you SHOULD enable `google_auth: true` as recommended, do not alter other configurations or disable features without a specific request.
|
|
175
177
|
|
|
176
178
|
</details>
|
|
177
179
|
|
|
@@ -328,10 +330,23 @@ Example workflow:
|
|
|
328
330
|
- Use camelCase for function names
|
|
329
331
|
```
|
|
330
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.
|
|
331
334
|
- **Anthropic Auto Compact**: Automatically compacts conversation history when approaching context limits for Anthropic models.
|
|
332
335
|
- **Empty Task Response Detector**: Detects when subagent tasks return empty or meaningless responses and handles gracefully.
|
|
333
336
|
- **Grep Output Truncator**: Prevents grep output from overwhelming the context by truncating excessively long results.
|
|
334
337
|
|
|
338
|
+
You can disable specific built-in hooks using `disabled_hooks` in `~/.config/opencode/oh-my-opencode.json` or `.opencode/oh-my-opencode.json`:
|
|
339
|
+
|
|
340
|
+
```json
|
|
341
|
+
{
|
|
342
|
+
"disabled_hooks": ["session-notification", "comment-checker"]
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
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`
|
|
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)).
|
|
349
|
+
|
|
335
350
|
### Claude Code Compatibility
|
|
336
351
|
|
|
337
352
|
Oh My OpenCode provides seamless Claude Code configuration compatibility. If you've been using Claude Code, your existing setup works out of the box.
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { OhMyOpenCodeConfigSchema, AgentOverrideConfigSchema, AgentOverridesSchema, McpNameSchema, AgentNameSchema, } from "./schema";
|
|
2
|
-
export type { OhMyOpenCodeConfig, AgentOverrideConfig, AgentOverrides, McpName, AgentName, } from "./schema";
|
|
1
|
+
export { OhMyOpenCodeConfigSchema, AgentOverrideConfigSchema, AgentOverridesSchema, McpNameSchema, AgentNameSchema, HookNameSchema, } from "./schema";
|
|
2
|
+
export type { OhMyOpenCodeConfig, AgentOverrideConfig, AgentOverrides, McpName, AgentName, HookName, } from "./schema";
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -6,6 +6,23 @@ export declare const AgentNameSchema: z.ZodEnum<{
|
|
|
6
6
|
"frontend-ui-ux-engineer": "frontend-ui-ux-engineer";
|
|
7
7
|
"document-writer": "document-writer";
|
|
8
8
|
}>;
|
|
9
|
+
export declare const HookNameSchema: z.ZodEnum<{
|
|
10
|
+
"comment-checker": "comment-checker";
|
|
11
|
+
"rules-injector": "rules-injector";
|
|
12
|
+
"todo-continuation-enforcer": "todo-continuation-enforcer";
|
|
13
|
+
"context-window-monitor": "context-window-monitor";
|
|
14
|
+
"session-recovery": "session-recovery";
|
|
15
|
+
"session-notification": "session-notification";
|
|
16
|
+
"grep-output-truncator": "grep-output-truncator";
|
|
17
|
+
"directory-agents-injector": "directory-agents-injector";
|
|
18
|
+
"directory-readme-injector": "directory-readme-injector";
|
|
19
|
+
"empty-task-response-detector": "empty-task-response-detector";
|
|
20
|
+
"think-mode": "think-mode";
|
|
21
|
+
"anthropic-auto-compact": "anthropic-auto-compact";
|
|
22
|
+
"background-notification": "background-notification";
|
|
23
|
+
"auto-update-checker": "auto-update-checker";
|
|
24
|
+
"ultrawork-mode": "ultrawork-mode";
|
|
25
|
+
}>;
|
|
9
26
|
export declare const AgentOverrideConfigSchema: z.ZodObject<{
|
|
10
27
|
model: z.ZodOptional<z.ZodString>;
|
|
11
28
|
temperature: z.ZodOptional<z.ZodNumber>;
|
|
@@ -304,6 +321,23 @@ export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
|
|
|
304
321
|
"frontend-ui-ux-engineer": "frontend-ui-ux-engineer";
|
|
305
322
|
"document-writer": "document-writer";
|
|
306
323
|
}>>>;
|
|
324
|
+
disabled_hooks: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
325
|
+
"comment-checker": "comment-checker";
|
|
326
|
+
"rules-injector": "rules-injector";
|
|
327
|
+
"todo-continuation-enforcer": "todo-continuation-enforcer";
|
|
328
|
+
"context-window-monitor": "context-window-monitor";
|
|
329
|
+
"session-recovery": "session-recovery";
|
|
330
|
+
"session-notification": "session-notification";
|
|
331
|
+
"grep-output-truncator": "grep-output-truncator";
|
|
332
|
+
"directory-agents-injector": "directory-agents-injector";
|
|
333
|
+
"directory-readme-injector": "directory-readme-injector";
|
|
334
|
+
"empty-task-response-detector": "empty-task-response-detector";
|
|
335
|
+
"think-mode": "think-mode";
|
|
336
|
+
"anthropic-auto-compact": "anthropic-auto-compact";
|
|
337
|
+
"background-notification": "background-notification";
|
|
338
|
+
"auto-update-checker": "auto-update-checker";
|
|
339
|
+
"ultrawork-mode": "ultrawork-mode";
|
|
340
|
+
}>>>;
|
|
307
341
|
agents: z.ZodOptional<z.ZodObject<{
|
|
308
342
|
oracle: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
309
343
|
model: z.ZodOptional<z.ZodString>;
|
|
@@ -549,4 +583,5 @@ export type OhMyOpenCodeConfig = z.infer<typeof OhMyOpenCodeConfigSchema>;
|
|
|
549
583
|
export type AgentOverrideConfig = z.infer<typeof AgentOverrideConfigSchema>;
|
|
550
584
|
export type AgentOverrides = z.infer<typeof AgentOverridesSchema>;
|
|
551
585
|
export type AgentName = z.infer<typeof AgentNameSchema>;
|
|
586
|
+
export type HookName = z.infer<typeof HookNameSchema>;
|
|
552
587
|
export { McpNameSchema, type McpName } from "../mcp/types";
|
|
@@ -3,6 +3,8 @@ export interface TaskProgress {
|
|
|
3
3
|
toolCalls: number;
|
|
4
4
|
lastTool?: string;
|
|
5
5
|
lastUpdate: Date;
|
|
6
|
+
lastMessage?: string;
|
|
7
|
+
lastMessageAt?: Date;
|
|
6
8
|
}
|
|
7
9
|
export interface BackgroundTask {
|
|
8
10
|
id: string;
|
|
@@ -10,6 +12,7 @@ export interface BackgroundTask {
|
|
|
10
12
|
parentSessionID: string;
|
|
11
13
|
parentMessageID: string;
|
|
12
14
|
description: string;
|
|
15
|
+
prompt: string;
|
|
13
16
|
agent: string;
|
|
14
17
|
status: BackgroundTaskStatus;
|
|
15
18
|
startedAt: Date;
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -13,3 +13,4 @@ export { createClaudeCodeHooksHook } from "./claude-code-hooks";
|
|
|
13
13
|
export { createRulesInjectorHook } from "./rules-injector";
|
|
14
14
|
export { createBackgroundNotificationHook } from "./background-notification";
|
|
15
15
|
export { createAutoUpdateCheckerHook } from "./auto-update-checker";
|
|
16
|
+
export { createUltraworkModeHook } from "./ultrawork-mode";
|
|
@@ -8,6 +8,8 @@ interface SessionNotificationConfig {
|
|
|
8
8
|
idleConfirmationDelay?: number;
|
|
9
9
|
/** Skip notification if there are incomplete todos (default: true) */
|
|
10
10
|
skipIfIncompleteTodos?: boolean;
|
|
11
|
+
/** Maximum number of sessions to track before cleanup (default: 100) */
|
|
12
|
+
maxTrackedSessions?: number;
|
|
11
13
|
}
|
|
12
14
|
export declare function createSessionNotification(ctx: PluginInput, config?: SessionNotificationConfig): ({ event }: {
|
|
13
15
|
event: {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Keyword patterns - "ultrawork", "ulw" (case-insensitive, word boundary) */
|
|
2
|
+
export declare const ULTRAWORK_PATTERNS: RegExp[];
|
|
3
|
+
/** Code block pattern to exclude from keyword detection */
|
|
4
|
+
export declare const CODE_BLOCK_PATTERN: RegExp;
|
|
5
|
+
/** Inline code pattern to exclude */
|
|
6
|
+
export declare const INLINE_CODE_PATTERN: RegExp;
|
|
7
|
+
/**
|
|
8
|
+
* ULTRAWORK_CONTEXT - Agent-Agnostic Guidance
|
|
9
|
+
*
|
|
10
|
+
* Key principles:
|
|
11
|
+
* - NO specific agent names (oracle, librarian, etc.)
|
|
12
|
+
* - Only provide guidance based on agent role/capability
|
|
13
|
+
* - Emphasize parallel execution, TODO tracking, delegation
|
|
14
|
+
*/
|
|
15
|
+
export declare const ULTRAWORK_CONTEXT = "<ultrawork-mode>\n[CODE RED] Maximum precision required. Ultrathink before acting.\n\nYOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL.\nTELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.\n\n## AGENT UTILIZATION PRINCIPLES (by capability, not by name)\n- **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS for file patterns, internal implementations, project structure\n- **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS for API references, examples, external library docs\n- **Planning & Strategy**: NEVER plan yourself - ALWAYS spawn a dedicated planning agent for work breakdown\n- **High-IQ Reasoning**: Leverage specialized agents for architecture decisions, code review, strategic planning\n- **Frontend/UI Tasks**: Delegate to UI-specialized agents for design and implementation\n\n## EXECUTION RULES\n- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each.\n- **PARALLEL**: Fire independent agent calls simultaneously via background_task - NEVER wait sequentially.\n- **BACKGROUND FIRST**: Use background_task for exploration/research agents (10+ concurrent if needed).\n- **VERIFY**: Re-read request after completion. Check ALL requirements met before reporting done.\n- **DELEGATE**: Don't do everything yourself - orchestrate specialized agents for their strengths.\n\n## WORKFLOW\n1. Analyze the request and identify required capabilities\n2. Spawn exploration/librarian agents via background_task in PARALLEL (10+ if needed)\n3. Use planning agents to create detailed work breakdown\n4. Execute with continuous verification against original requirements\n\n</ultrawork-mode>\n\n---\n\n";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove code blocks and inline code from text.
|
|
3
|
+
* Prevents false positives when keywords appear in code.
|
|
4
|
+
*/
|
|
5
|
+
export declare function removeCodeBlocks(text: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Detect ultrawork keywords in text (excluding code blocks).
|
|
8
|
+
*/
|
|
9
|
+
export declare function detectUltraworkKeyword(text: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Extract text content from message parts.
|
|
12
|
+
*/
|
|
13
|
+
export declare function extractPromptText(parts: Array<{
|
|
14
|
+
type: string;
|
|
15
|
+
text?: string;
|
|
16
|
+
}>): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export * from "./detector";
|
|
2
|
+
export * from "./constants";
|
|
3
|
+
export * from "./types";
|
|
4
|
+
export declare function clearUltraworkModeState(sessionID: string): void;
|
|
5
|
+
export declare function createUltraworkModeHook(): {
|
|
6
|
+
/**
|
|
7
|
+
* chat.message hook - detect ultrawork/ulw keywords, inject context via history
|
|
8
|
+
*
|
|
9
|
+
* Execution timing: AFTER claudeCodeHooks["chat.message"]
|
|
10
|
+
* Behavior:
|
|
11
|
+
* 1. Extract text from user prompt
|
|
12
|
+
* 2. Detect ultrawork/ulw keywords (excluding code blocks)
|
|
13
|
+
* 3. If detected, inject ULTRAWORK_CONTEXT via injectHookMessage (history injection)
|
|
14
|
+
*/
|
|
15
|
+
"chat.message": (input: {
|
|
16
|
+
sessionID: string;
|
|
17
|
+
agent?: string;
|
|
18
|
+
model?: {
|
|
19
|
+
providerID: string;
|
|
20
|
+
modelID: string;
|
|
21
|
+
};
|
|
22
|
+
messageID?: string;
|
|
23
|
+
}, output: {
|
|
24
|
+
message: Record<string, unknown>;
|
|
25
|
+
parts: Array<{
|
|
26
|
+
type: string;
|
|
27
|
+
text?: string;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}>;
|
|
30
|
+
}) => Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* event hook - cleanup session state on deletion
|
|
33
|
+
*/
|
|
34
|
+
event: ({ event, }: {
|
|
35
|
+
event: {
|
|
36
|
+
type: string;
|
|
37
|
+
properties?: unknown;
|
|
38
|
+
};
|
|
39
|
+
}) => Promise<void>;
|
|
40
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface UltraworkModeState {
|
|
2
|
+
/** Whether ultrawork keyword was detected */
|
|
3
|
+
detected: boolean;
|
|
4
|
+
/** Whether context was injected */
|
|
5
|
+
injected: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface ModelRef {
|
|
8
|
+
providerID: string;
|
|
9
|
+
modelID: string;
|
|
10
|
+
}
|
|
11
|
+
export interface MessageWithModel {
|
|
12
|
+
model?: ModelRef;
|
|
13
|
+
}
|
|
14
|
+
export interface UltraworkModeInput {
|
|
15
|
+
parts: Array<{
|
|
16
|
+
type: string;
|
|
17
|
+
text?: string;
|
|
18
|
+
}>;
|
|
19
|
+
message: MessageWithModel;
|
|
20
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
2
|
declare const OhMyOpenCodePlugin: Plugin;
|
|
3
3
|
export default OhMyOpenCodePlugin;
|
|
4
|
-
export type { OhMyOpenCodeConfig, AgentName, AgentOverrideConfig, AgentOverrides, McpName, } from "./config";
|
|
4
|
+
export type { OhMyOpenCodeConfig, AgentName, AgentOverrideConfig, AgentOverrides, McpName, HookName, } from "./config";
|
package/dist/index.js
CHANGED
|
@@ -184,8 +184,8 @@ var require_utils = __commonJS((exports) => {
|
|
|
184
184
|
exports.toPosixSlashes = (str) => str.replace(REGEX_BACKSLASH, "/");
|
|
185
185
|
exports.isWindows = () => {
|
|
186
186
|
if (typeof navigator !== "undefined" && navigator.platform) {
|
|
187
|
-
const
|
|
188
|
-
return
|
|
187
|
+
const platform2 = navigator.platform.toLowerCase();
|
|
188
|
+
return platform2 === "win32" || platform2 === "windows";
|
|
189
189
|
}
|
|
190
190
|
if (typeof process !== "undefined" && process.platform) {
|
|
191
191
|
return process.platform === "win32";
|
|
@@ -2934,6 +2934,198 @@ ${CONTEXT_REMINDER}
|
|
|
2934
2934
|
event: eventHandler
|
|
2935
2935
|
};
|
|
2936
2936
|
}
|
|
2937
|
+
// src/hooks/session-notification.ts
|
|
2938
|
+
import { platform } from "os";
|
|
2939
|
+
function detectPlatform() {
|
|
2940
|
+
const p = platform();
|
|
2941
|
+
if (p === "darwin" || p === "linux" || p === "win32")
|
|
2942
|
+
return p;
|
|
2943
|
+
return "unsupported";
|
|
2944
|
+
}
|
|
2945
|
+
function getDefaultSoundPath(p) {
|
|
2946
|
+
switch (p) {
|
|
2947
|
+
case "darwin":
|
|
2948
|
+
return "/System/Library/Sounds/Glass.aiff";
|
|
2949
|
+
case "linux":
|
|
2950
|
+
return "/usr/share/sounds/freedesktop/stereo/complete.oga";
|
|
2951
|
+
case "win32":
|
|
2952
|
+
return "C:\\Windows\\Media\\notify.wav";
|
|
2953
|
+
default:
|
|
2954
|
+
return "";
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
async function sendNotification(ctx, p, title, message) {
|
|
2958
|
+
const escapedTitle = title.replace(/"/g, "\\\"").replace(/'/g, "\\'");
|
|
2959
|
+
const escapedMessage = message.replace(/"/g, "\\\"").replace(/'/g, "\\'");
|
|
2960
|
+
switch (p) {
|
|
2961
|
+
case "darwin":
|
|
2962
|
+
await ctx.$`osascript -e ${'display notification "' + escapedMessage + '" with title "' + escapedTitle + '"'}`;
|
|
2963
|
+
break;
|
|
2964
|
+
case "linux":
|
|
2965
|
+
await ctx.$`notify-send ${escapedTitle} ${escapedMessage}`;
|
|
2966
|
+
break;
|
|
2967
|
+
case "win32":
|
|
2968
|
+
await ctx.$`powershell -Command ${"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('" + escapedMessage + "', '" + escapedTitle + "')"}`;
|
|
2969
|
+
break;
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
async function playSound(ctx, p, soundPath) {
|
|
2973
|
+
switch (p) {
|
|
2974
|
+
case "darwin":
|
|
2975
|
+
ctx.$`afplay ${soundPath}`.catch(() => {});
|
|
2976
|
+
break;
|
|
2977
|
+
case "linux":
|
|
2978
|
+
ctx.$`paplay ${soundPath}`.catch(() => {
|
|
2979
|
+
ctx.$`aplay ${soundPath}`.catch(() => {});
|
|
2980
|
+
});
|
|
2981
|
+
break;
|
|
2982
|
+
case "win32":
|
|
2983
|
+
ctx.$`powershell -Command ${"(New-Object Media.SoundPlayer '" + soundPath + "').PlaySync()"}`.catch(() => {});
|
|
2984
|
+
break;
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
async function hasIncompleteTodos(ctx, sessionID) {
|
|
2988
|
+
try {
|
|
2989
|
+
const response = await ctx.client.session.todo({ path: { id: sessionID } });
|
|
2990
|
+
const todos = response.data ?? response;
|
|
2991
|
+
if (!todos || todos.length === 0)
|
|
2992
|
+
return false;
|
|
2993
|
+
return todos.some((t) => t.status !== "completed" && t.status !== "cancelled");
|
|
2994
|
+
} catch {
|
|
2995
|
+
return false;
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
function createSessionNotification(ctx, config = {}) {
|
|
2999
|
+
const currentPlatform = detectPlatform();
|
|
3000
|
+
const defaultSoundPath = getDefaultSoundPath(currentPlatform);
|
|
3001
|
+
const mergedConfig = {
|
|
3002
|
+
title: "OpenCode",
|
|
3003
|
+
message: "Agent is ready for input",
|
|
3004
|
+
playSound: false,
|
|
3005
|
+
soundPath: defaultSoundPath,
|
|
3006
|
+
idleConfirmationDelay: 1500,
|
|
3007
|
+
skipIfIncompleteTodos: true,
|
|
3008
|
+
maxTrackedSessions: 100,
|
|
3009
|
+
...config
|
|
3010
|
+
};
|
|
3011
|
+
const notifiedSessions = new Set;
|
|
3012
|
+
const pendingTimers = new Map;
|
|
3013
|
+
const sessionActivitySinceIdle = new Set;
|
|
3014
|
+
const notificationVersions = new Map;
|
|
3015
|
+
function cleanupOldSessions() {
|
|
3016
|
+
const maxSessions = mergedConfig.maxTrackedSessions;
|
|
3017
|
+
if (notifiedSessions.size > maxSessions) {
|
|
3018
|
+
const sessionsToRemove = Array.from(notifiedSessions).slice(0, notifiedSessions.size - maxSessions);
|
|
3019
|
+
sessionsToRemove.forEach((id) => notifiedSessions.delete(id));
|
|
3020
|
+
}
|
|
3021
|
+
if (sessionActivitySinceIdle.size > maxSessions) {
|
|
3022
|
+
const sessionsToRemove = Array.from(sessionActivitySinceIdle).slice(0, sessionActivitySinceIdle.size - maxSessions);
|
|
3023
|
+
sessionsToRemove.forEach((id) => sessionActivitySinceIdle.delete(id));
|
|
3024
|
+
}
|
|
3025
|
+
if (notificationVersions.size > maxSessions) {
|
|
3026
|
+
const sessionsToRemove = Array.from(notificationVersions.keys()).slice(0, notificationVersions.size - maxSessions);
|
|
3027
|
+
sessionsToRemove.forEach((id) => notificationVersions.delete(id));
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
function cancelPendingNotification(sessionID) {
|
|
3031
|
+
const timer = pendingTimers.get(sessionID);
|
|
3032
|
+
if (timer) {
|
|
3033
|
+
clearTimeout(timer);
|
|
3034
|
+
pendingTimers.delete(sessionID);
|
|
3035
|
+
}
|
|
3036
|
+
sessionActivitySinceIdle.add(sessionID);
|
|
3037
|
+
notificationVersions.set(sessionID, (notificationVersions.get(sessionID) ?? 0) + 1);
|
|
3038
|
+
}
|
|
3039
|
+
function markSessionActivity(sessionID) {
|
|
3040
|
+
cancelPendingNotification(sessionID);
|
|
3041
|
+
notifiedSessions.delete(sessionID);
|
|
3042
|
+
}
|
|
3043
|
+
async function executeNotification(sessionID, version) {
|
|
3044
|
+
pendingTimers.delete(sessionID);
|
|
3045
|
+
if (notificationVersions.get(sessionID) !== version) {
|
|
3046
|
+
return;
|
|
3047
|
+
}
|
|
3048
|
+
if (sessionActivitySinceIdle.has(sessionID)) {
|
|
3049
|
+
sessionActivitySinceIdle.delete(sessionID);
|
|
3050
|
+
return;
|
|
3051
|
+
}
|
|
3052
|
+
if (notifiedSessions.has(sessionID))
|
|
3053
|
+
return;
|
|
3054
|
+
if (mergedConfig.skipIfIncompleteTodos) {
|
|
3055
|
+
const hasPendingWork = await hasIncompleteTodos(ctx, sessionID);
|
|
3056
|
+
if (notificationVersions.get(sessionID) !== version) {
|
|
3057
|
+
return;
|
|
3058
|
+
}
|
|
3059
|
+
if (hasPendingWork)
|
|
3060
|
+
return;
|
|
3061
|
+
}
|
|
3062
|
+
if (notificationVersions.get(sessionID) !== version) {
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
3065
|
+
notifiedSessions.add(sessionID);
|
|
3066
|
+
try {
|
|
3067
|
+
await sendNotification(ctx, currentPlatform, mergedConfig.title, mergedConfig.message);
|
|
3068
|
+
if (mergedConfig.playSound && mergedConfig.soundPath) {
|
|
3069
|
+
await playSound(ctx, currentPlatform, mergedConfig.soundPath);
|
|
3070
|
+
}
|
|
3071
|
+
} catch {}
|
|
3072
|
+
}
|
|
3073
|
+
return async ({ event }) => {
|
|
3074
|
+
if (currentPlatform === "unsupported")
|
|
3075
|
+
return;
|
|
3076
|
+
const props = event.properties;
|
|
3077
|
+
if (event.type === "session.updated" || event.type === "session.created") {
|
|
3078
|
+
const info = props?.info;
|
|
3079
|
+
const sessionID = info?.id;
|
|
3080
|
+
if (sessionID) {
|
|
3081
|
+
markSessionActivity(sessionID);
|
|
3082
|
+
}
|
|
3083
|
+
return;
|
|
3084
|
+
}
|
|
3085
|
+
if (event.type === "session.idle") {
|
|
3086
|
+
const sessionID = props?.sessionID;
|
|
3087
|
+
if (!sessionID)
|
|
3088
|
+
return;
|
|
3089
|
+
if (notifiedSessions.has(sessionID))
|
|
3090
|
+
return;
|
|
3091
|
+
if (pendingTimers.has(sessionID))
|
|
3092
|
+
return;
|
|
3093
|
+
sessionActivitySinceIdle.delete(sessionID);
|
|
3094
|
+
const currentVersion = (notificationVersions.get(sessionID) ?? 0) + 1;
|
|
3095
|
+
notificationVersions.set(sessionID, currentVersion);
|
|
3096
|
+
const timer = setTimeout(() => {
|
|
3097
|
+
executeNotification(sessionID, currentVersion);
|
|
3098
|
+
}, mergedConfig.idleConfirmationDelay);
|
|
3099
|
+
pendingTimers.set(sessionID, timer);
|
|
3100
|
+
cleanupOldSessions();
|
|
3101
|
+
return;
|
|
3102
|
+
}
|
|
3103
|
+
if (event.type === "message.updated" || event.type === "message.created") {
|
|
3104
|
+
const info = props?.info;
|
|
3105
|
+
const sessionID = info?.sessionID;
|
|
3106
|
+
if (sessionID) {
|
|
3107
|
+
markSessionActivity(sessionID);
|
|
3108
|
+
}
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
if (event.type === "tool.execute.before" || event.type === "tool.execute.after") {
|
|
3112
|
+
const sessionID = props?.sessionID;
|
|
3113
|
+
if (sessionID) {
|
|
3114
|
+
markSessionActivity(sessionID);
|
|
3115
|
+
}
|
|
3116
|
+
return;
|
|
3117
|
+
}
|
|
3118
|
+
if (event.type === "session.deleted") {
|
|
3119
|
+
const sessionInfo = props?.info;
|
|
3120
|
+
if (sessionInfo?.id) {
|
|
3121
|
+
cancelPendingNotification(sessionInfo.id);
|
|
3122
|
+
notifiedSessions.delete(sessionInfo.id);
|
|
3123
|
+
sessionActivitySinceIdle.delete(sessionInfo.id);
|
|
3124
|
+
notificationVersions.delete(sessionInfo.id);
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
};
|
|
3128
|
+
}
|
|
2937
3129
|
// src/hooks/session-recovery/storage.ts
|
|
2938
3130
|
import { existsSync as existsSync3, mkdirSync, readdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
|
|
2939
3131
|
import { join as join4 } from "path";
|
|
@@ -4625,6 +4817,46 @@ var ALREADY_HIGH = new Set([
|
|
|
4625
4817
|
"gpt-5.2-chat-latest-high",
|
|
4626
4818
|
"gpt-5.2-pro-high"
|
|
4627
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
|
+
};
|
|
4628
4860
|
function getHighVariant(modelID) {
|
|
4629
4861
|
if (ALREADY_HIGH.has(modelID)) {
|
|
4630
4862
|
return null;
|
|
@@ -4634,6 +4866,19 @@ function getHighVariant(modelID) {
|
|
|
4634
4866
|
function isAlreadyHighVariant(modelID) {
|
|
4635
4867
|
return ALREADY_HIGH.has(modelID) || modelID.endsWith("-high");
|
|
4636
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
|
+
}
|
|
4637
4882
|
|
|
4638
4883
|
// src/hooks/think-mode/index.ts
|
|
4639
4884
|
var thinkModeState = new Map;
|
|
@@ -4643,7 +4888,8 @@ function createThinkModeHook() {
|
|
|
4643
4888
|
const promptText = extractPromptText(output.parts);
|
|
4644
4889
|
const state = {
|
|
4645
4890
|
requested: false,
|
|
4646
|
-
modelSwitched: false
|
|
4891
|
+
modelSwitched: false,
|
|
4892
|
+
thinkingConfigInjected: false
|
|
4647
4893
|
};
|
|
4648
4894
|
if (!detectThinkKeyword(promptText)) {
|
|
4649
4895
|
thinkModeState.set(sessionID, state);
|
|
@@ -4662,15 +4908,28 @@ function createThinkModeHook() {
|
|
|
4662
4908
|
return;
|
|
4663
4909
|
}
|
|
4664
4910
|
const highVariant = getHighVariant(currentModel.modelID);
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
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
|
+
});
|
|
4668
4932
|
}
|
|
4669
|
-
output.message.model = {
|
|
4670
|
-
providerID: currentModel.providerID,
|
|
4671
|
-
modelID: highVariant
|
|
4672
|
-
};
|
|
4673
|
-
state.modelSwitched = true;
|
|
4674
4933
|
thinkModeState.set(sessionID, state);
|
|
4675
4934
|
},
|
|
4676
4935
|
event: async ({ event }) => {
|
|
@@ -6330,6 +6589,97 @@ function createAutoUpdateCheckerHook(ctx) {
|
|
|
6330
6589
|
}
|
|
6331
6590
|
};
|
|
6332
6591
|
}
|
|
6592
|
+
// src/hooks/ultrawork-mode/constants.ts
|
|
6593
|
+
var ULTRAWORK_PATTERNS = [/\bultrawork\b/i, /\bulw\b/i];
|
|
6594
|
+
var CODE_BLOCK_PATTERN2 = /```[\s\S]*?```/g;
|
|
6595
|
+
var INLINE_CODE_PATTERN2 = /`[^`]+`/g;
|
|
6596
|
+
var ULTRAWORK_CONTEXT = `<ultrawork-mode>
|
|
6597
|
+
[CODE RED] Maximum precision required. Ultrathink before acting.
|
|
6598
|
+
|
|
6599
|
+
YOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL.
|
|
6600
|
+
TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
|
|
6601
|
+
|
|
6602
|
+
## AGENT UTILIZATION PRINCIPLES (by capability, not by name)
|
|
6603
|
+
- **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS for file patterns, internal implementations, project structure
|
|
6604
|
+
- **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS for API references, examples, external library docs
|
|
6605
|
+
- **Planning & Strategy**: NEVER plan yourself - ALWAYS spawn a dedicated planning agent for work breakdown
|
|
6606
|
+
- **High-IQ Reasoning**: Leverage specialized agents for architecture decisions, code review, strategic planning
|
|
6607
|
+
- **Frontend/UI Tasks**: Delegate to UI-specialized agents for design and implementation
|
|
6608
|
+
|
|
6609
|
+
## EXECUTION RULES
|
|
6610
|
+
- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each.
|
|
6611
|
+
- **PARALLEL**: Fire independent agent calls simultaneously via background_task - NEVER wait sequentially.
|
|
6612
|
+
- **BACKGROUND FIRST**: Use background_task for exploration/research agents (10+ concurrent if needed).
|
|
6613
|
+
- **VERIFY**: Re-read request after completion. Check ALL requirements met before reporting done.
|
|
6614
|
+
- **DELEGATE**: Don't do everything yourself - orchestrate specialized agents for their strengths.
|
|
6615
|
+
|
|
6616
|
+
## WORKFLOW
|
|
6617
|
+
1. Analyze the request and identify required capabilities
|
|
6618
|
+
2. Spawn exploration/librarian agents via background_task in PARALLEL (10+ if needed)
|
|
6619
|
+
3. Use planning agents to create detailed work breakdown
|
|
6620
|
+
4. Execute with continuous verification against original requirements
|
|
6621
|
+
|
|
6622
|
+
</ultrawork-mode>
|
|
6623
|
+
|
|
6624
|
+
---
|
|
6625
|
+
|
|
6626
|
+
`;
|
|
6627
|
+
|
|
6628
|
+
// src/hooks/ultrawork-mode/detector.ts
|
|
6629
|
+
function removeCodeBlocks2(text) {
|
|
6630
|
+
return text.replace(CODE_BLOCK_PATTERN2, "").replace(INLINE_CODE_PATTERN2, "");
|
|
6631
|
+
}
|
|
6632
|
+
function detectUltraworkKeyword(text) {
|
|
6633
|
+
const textWithoutCode = removeCodeBlocks2(text);
|
|
6634
|
+
return ULTRAWORK_PATTERNS.some((pattern) => pattern.test(textWithoutCode));
|
|
6635
|
+
}
|
|
6636
|
+
function extractPromptText2(parts) {
|
|
6637
|
+
return parts.filter((p) => p.type === "text").map((p) => p.text || "").join("");
|
|
6638
|
+
}
|
|
6639
|
+
|
|
6640
|
+
// src/hooks/ultrawork-mode/index.ts
|
|
6641
|
+
var ultraworkModeState = new Map;
|
|
6642
|
+
function createUltraworkModeHook() {
|
|
6643
|
+
return {
|
|
6644
|
+
"chat.message": async (input, output) => {
|
|
6645
|
+
const state = {
|
|
6646
|
+
detected: false,
|
|
6647
|
+
injected: false
|
|
6648
|
+
};
|
|
6649
|
+
const promptText = extractPromptText2(output.parts);
|
|
6650
|
+
if (!detectUltraworkKeyword(promptText)) {
|
|
6651
|
+
ultraworkModeState.set(input.sessionID, state);
|
|
6652
|
+
return;
|
|
6653
|
+
}
|
|
6654
|
+
state.detected = true;
|
|
6655
|
+
log("Ultrawork keyword detected", { sessionID: input.sessionID });
|
|
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) {
|
|
6664
|
+
state.injected = true;
|
|
6665
|
+
log("Ultrawork context injected via history", { sessionID: input.sessionID });
|
|
6666
|
+
} else {
|
|
6667
|
+
log("Ultrawork context injection failed", { sessionID: input.sessionID });
|
|
6668
|
+
}
|
|
6669
|
+
ultraworkModeState.set(input.sessionID, state);
|
|
6670
|
+
},
|
|
6671
|
+
event: async ({
|
|
6672
|
+
event
|
|
6673
|
+
}) => {
|
|
6674
|
+
if (event.type === "session.deleted") {
|
|
6675
|
+
const props = event.properties;
|
|
6676
|
+
if (props?.info?.id) {
|
|
6677
|
+
ultraworkModeState.delete(props.info.id);
|
|
6678
|
+
}
|
|
6679
|
+
}
|
|
6680
|
+
}
|
|
6681
|
+
};
|
|
6682
|
+
}
|
|
6333
6683
|
// src/auth/antigravity/constants.ts
|
|
6334
6684
|
var ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
|
|
6335
6685
|
var ANTIGRAVITY_CLIENT_SECRET = "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";
|
|
@@ -7434,6 +7784,18 @@ function isRetryableError(status) {
|
|
|
7434
7784
|
return true;
|
|
7435
7785
|
return false;
|
|
7436
7786
|
}
|
|
7787
|
+
var GCP_PERMISSION_ERROR_PATTERNS = [
|
|
7788
|
+
"PERMISSION_DENIED",
|
|
7789
|
+
"does not have permission",
|
|
7790
|
+
"Cloud AI Companion API has not been used",
|
|
7791
|
+
"has not been enabled"
|
|
7792
|
+
];
|
|
7793
|
+
function isGcpPermissionError(text) {
|
|
7794
|
+
return GCP_PERMISSION_ERROR_PATTERNS.some((pattern) => text.includes(pattern));
|
|
7795
|
+
}
|
|
7796
|
+
function calculateRetryDelay2(attempt) {
|
|
7797
|
+
return Math.min(200 * Math.pow(2, attempt), 2000);
|
|
7798
|
+
}
|
|
7437
7799
|
async function isRetryableResponse(response) {
|
|
7438
7800
|
if (isRetryableError(response.status))
|
|
7439
7801
|
return true;
|
|
@@ -7496,18 +7858,36 @@ async function attemptFetch(options) {
|
|
|
7496
7858
|
thoughtSignature
|
|
7497
7859
|
});
|
|
7498
7860
|
debugLog6(`[REQ] streaming=${transformed.streaming}, url=${transformed.url}`);
|
|
7499
|
-
const
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
debugLog6(`
|
|
7508
|
-
|
|
7861
|
+
const maxPermissionRetries = 10;
|
|
7862
|
+
for (let attempt = 0;attempt <= maxPermissionRetries; attempt++) {
|
|
7863
|
+
const response = await fetch(transformed.url, {
|
|
7864
|
+
method: init.method || "POST",
|
|
7865
|
+
headers: transformed.headers,
|
|
7866
|
+
body: JSON.stringify(transformed.body),
|
|
7867
|
+
signal: init.signal
|
|
7868
|
+
});
|
|
7869
|
+
debugLog6(`[RESP] status=${response.status} content-type=${response.headers.get("content-type") ?? ""} url=${response.url}`);
|
|
7870
|
+
if (response.status === 403) {
|
|
7871
|
+
try {
|
|
7872
|
+
const text = await response.clone().text();
|
|
7873
|
+
if (isGcpPermissionError(text)) {
|
|
7874
|
+
if (attempt < maxPermissionRetries) {
|
|
7875
|
+
const delay = calculateRetryDelay2(attempt);
|
|
7876
|
+
debugLog6(`[RETRY] GCP permission error, retry ${attempt + 1}/${maxPermissionRetries} after ${delay}ms`);
|
|
7877
|
+
await new Promise((resolve4) => setTimeout(resolve4, delay));
|
|
7878
|
+
continue;
|
|
7879
|
+
}
|
|
7880
|
+
debugLog6(`[RETRY] GCP permission error, max retries exceeded`);
|
|
7881
|
+
}
|
|
7882
|
+
} catch {}
|
|
7883
|
+
}
|
|
7884
|
+
if (!response.ok && await isRetryableResponse(response)) {
|
|
7885
|
+
debugLog6(`Endpoint failed: ${endpoint} (status: ${response.status}), trying next`);
|
|
7886
|
+
return null;
|
|
7887
|
+
}
|
|
7888
|
+
return response;
|
|
7509
7889
|
}
|
|
7510
|
-
return
|
|
7890
|
+
return null;
|
|
7511
7891
|
} catch (error) {
|
|
7512
7892
|
debugLog6(`Endpoint failed: ${endpoint} (${error instanceof Error ? error.message : "Unknown error"}), trying next`);
|
|
7513
7893
|
return null;
|
|
@@ -7883,7 +8263,7 @@ function loadOpencodeProjectCommands() {
|
|
|
7883
8263
|
return commandsToRecord(commands);
|
|
7884
8264
|
}
|
|
7885
8265
|
// src/features/claude-code-skill-loader/loader.ts
|
|
7886
|
-
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";
|
|
7887
8267
|
import { homedir as homedir10 } from "os";
|
|
7888
8268
|
import { join as join25, resolve as resolve4 } from "path";
|
|
7889
8269
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
@@ -7899,8 +8279,12 @@ function loadSkillsFromDir(skillsDir, scope) {
|
|
|
7899
8279
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
7900
8280
|
continue;
|
|
7901
8281
|
let resolvedPath = skillPath;
|
|
7902
|
-
|
|
7903
|
-
|
|
8282
|
+
try {
|
|
8283
|
+
if (lstatSync(skillPath, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
8284
|
+
resolvedPath = resolve4(skillPath, "..", readlinkSync(skillPath));
|
|
8285
|
+
}
|
|
8286
|
+
} catch {
|
|
8287
|
+
continue;
|
|
7904
8288
|
}
|
|
7905
8289
|
const skillMdPath = join25(resolvedPath, "SKILL.md");
|
|
7906
8290
|
if (!existsSync20(skillMdPath))
|
|
@@ -19435,10 +19819,10 @@ function _property(property, schema, params) {
|
|
|
19435
19819
|
...normalizeParams(params)
|
|
19436
19820
|
});
|
|
19437
19821
|
}
|
|
19438
|
-
function _mime(
|
|
19822
|
+
function _mime(types10, params) {
|
|
19439
19823
|
return new $ZodCheckMimeType({
|
|
19440
19824
|
check: "mime_type",
|
|
19441
|
-
mime:
|
|
19825
|
+
mime: types10,
|
|
19442
19826
|
...normalizeParams(params)
|
|
19443
19827
|
});
|
|
19444
19828
|
}
|
|
@@ -21348,7 +21732,7 @@ var ZodFile = /* @__PURE__ */ $constructor("ZodFile", (inst, def) => {
|
|
|
21348
21732
|
ZodType.init(inst, def);
|
|
21349
21733
|
inst.min = (size, params) => inst.check(_minSize(size, params));
|
|
21350
21734
|
inst.max = (size, params) => inst.check(_maxSize(size, params));
|
|
21351
|
-
inst.mime = (
|
|
21735
|
+
inst.mime = (types10, params) => inst.check(_mime(Array.isArray(types10) ? types10 : [types10], params));
|
|
21352
21736
|
});
|
|
21353
21737
|
function file(params) {
|
|
21354
21738
|
return _file(ZodFile, params);
|
|
@@ -22001,7 +22385,7 @@ var lsp_code_action_resolve = tool({
|
|
|
22001
22385
|
// src/tools/ast-grep/constants.ts
|
|
22002
22386
|
import { createRequire as createRequire4 } from "module";
|
|
22003
22387
|
import { dirname as dirname5, join as join30 } from "path";
|
|
22004
|
-
import { existsSync as existsSync26, statSync as
|
|
22388
|
+
import { existsSync as existsSync26, statSync as statSync3 } from "fs";
|
|
22005
22389
|
|
|
22006
22390
|
// src/tools/ast-grep/downloader.ts
|
|
22007
22391
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
@@ -22115,13 +22499,13 @@ async function ensureAstGrepBinary() {
|
|
|
22115
22499
|
// src/tools/ast-grep/constants.ts
|
|
22116
22500
|
function isValidBinary(filePath) {
|
|
22117
22501
|
try {
|
|
22118
|
-
return
|
|
22502
|
+
return statSync3(filePath).size > 1e4;
|
|
22119
22503
|
} catch {
|
|
22120
22504
|
return false;
|
|
22121
22505
|
}
|
|
22122
22506
|
}
|
|
22123
22507
|
function getPlatformPackageName() {
|
|
22124
|
-
const
|
|
22508
|
+
const platform2 = process.platform;
|
|
22125
22509
|
const arch = process.arch;
|
|
22126
22510
|
const platformMap = {
|
|
22127
22511
|
"darwin-arm64": "@ast-grep/cli-darwin-arm64",
|
|
@@ -22132,7 +22516,7 @@ function getPlatformPackageName() {
|
|
|
22132
22516
|
"win32-arm64": "@ast-grep/cli-win32-arm64-msvc",
|
|
22133
22517
|
"win32-ia32": "@ast-grep/cli-win32-ia32-msvc"
|
|
22134
22518
|
};
|
|
22135
|
-
return platformMap[`${
|
|
22519
|
+
return platformMap[`${platform2}-${arch}`] ?? null;
|
|
22136
22520
|
}
|
|
22137
22521
|
function findSgCliPathSync() {
|
|
22138
22522
|
const binaryName = process.platform === "win32" ? "sg.exe" : "sg";
|
|
@@ -23135,7 +23519,7 @@ var SkillFrontmatterSchema = exports_external.object({
|
|
|
23135
23519
|
metadata: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
23136
23520
|
});
|
|
23137
23521
|
// src/tools/skill/tools.ts
|
|
23138
|
-
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";
|
|
23139
23523
|
import { homedir as homedir16 } from "os";
|
|
23140
23524
|
import { join as join33, resolve as resolve7, basename as basename4 } from "path";
|
|
23141
23525
|
function parseSkillFrontmatter(data) {
|
|
@@ -23160,7 +23544,7 @@ function discoverSkillsFromDir(skillsDir, scope) {
|
|
|
23160
23544
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
23161
23545
|
let resolvedPath = skillPath;
|
|
23162
23546
|
try {
|
|
23163
|
-
const stats =
|
|
23547
|
+
const stats = lstatSync2(skillPath, { throwIfNoEntry: false });
|
|
23164
23548
|
if (stats?.isSymbolicLink()) {
|
|
23165
23549
|
resolvedPath = resolve7(skillPath, "..", readlinkSync2(skillPath));
|
|
23166
23550
|
}
|
|
@@ -23197,7 +23581,7 @@ var skillListForDescription = availableSkills.map((s) => `- ${s.name}: ${s.descr
|
|
|
23197
23581
|
`);
|
|
23198
23582
|
function resolveSymlink(skillPath) {
|
|
23199
23583
|
try {
|
|
23200
|
-
const stats =
|
|
23584
|
+
const stats = lstatSync2(skillPath, { throwIfNoEntry: false });
|
|
23201
23585
|
if (stats?.isSymbolicLink()) {
|
|
23202
23586
|
return resolve7(skillPath, "..", readlinkSync2(skillPath));
|
|
23203
23587
|
}
|
|
@@ -23479,20 +23863,48 @@ Use \`background_output\` tool with task_id="${task.id}" to check progress:
|
|
|
23479
23863
|
function delay(ms) {
|
|
23480
23864
|
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
23481
23865
|
}
|
|
23866
|
+
function truncateText(text, maxLength) {
|
|
23867
|
+
if (text.length <= maxLength)
|
|
23868
|
+
return text;
|
|
23869
|
+
return text.slice(0, maxLength) + "...";
|
|
23870
|
+
}
|
|
23482
23871
|
function formatTaskStatus(task) {
|
|
23483
23872
|
const duration3 = formatDuration(task.startedAt, task.completedAt);
|
|
23484
|
-
const
|
|
23873
|
+
const promptPreview = truncateText(task.prompt, 500);
|
|
23874
|
+
let progressSection = "";
|
|
23875
|
+
if (task.progress) {
|
|
23876
|
+
progressSection = `
|
|
23485
23877
|
Tool calls: ${task.progress.toolCalls}
|
|
23486
|
-
Last tool: ${task.progress.lastTool ?? "N/A"}
|
|
23487
|
-
|
|
23878
|
+
Last tool: ${task.progress.lastTool ?? "N/A"}`;
|
|
23879
|
+
}
|
|
23880
|
+
let lastMessageSection = "";
|
|
23881
|
+
if (task.progress?.lastMessage) {
|
|
23882
|
+
const truncated = truncateText(task.progress.lastMessage, 500);
|
|
23883
|
+
const messageTime = task.progress.lastMessageAt ? task.progress.lastMessageAt.toISOString() : "N/A";
|
|
23884
|
+
lastMessageSection = `
|
|
23488
23885
|
|
|
23489
|
-
|
|
23490
|
-
|
|
23491
|
-
|
|
23492
|
-
|
|
23493
|
-
|
|
23886
|
+
## Last Message (${messageTime})
|
|
23887
|
+
|
|
23888
|
+
\`\`\`
|
|
23889
|
+
${truncated}
|
|
23890
|
+
\`\`\``;
|
|
23891
|
+
}
|
|
23892
|
+
return `# Task Status
|
|
23893
|
+
|
|
23894
|
+
| Field | Value |
|
|
23895
|
+
|-------|-------|
|
|
23896
|
+
| Task ID | \`${task.id}\` |
|
|
23897
|
+
| Description | ${task.description} |
|
|
23898
|
+
| Agent | ${task.agent} |
|
|
23899
|
+
| Status | **${task.status}** |
|
|
23900
|
+
| Duration | ${duration3} |
|
|
23901
|
+
| Session ID | \`${task.sessionID}\` |${progressSection}
|
|
23494
23902
|
|
|
23495
|
-
|
|
23903
|
+
## Original Prompt
|
|
23904
|
+
|
|
23905
|
+
\`\`\`
|
|
23906
|
+
${promptPreview}
|
|
23907
|
+
\`\`\`${lastMessageSection}`;
|
|
23496
23908
|
}
|
|
23497
23909
|
async function formatTaskResult(task, client2) {
|
|
23498
23910
|
const messagesResult = await client2.session.messages({
|
|
@@ -23834,6 +24246,7 @@ class BackgroundManager {
|
|
|
23834
24246
|
parentSessionID: input.parentSessionID,
|
|
23835
24247
|
parentMessageID: input.parentMessageID,
|
|
23836
24248
|
description: input.description,
|
|
24249
|
+
prompt: input.prompt,
|
|
23837
24250
|
agent: input.agent,
|
|
23838
24251
|
status: "running",
|
|
23839
24252
|
startedAt: new Date,
|
|
@@ -23991,23 +24404,18 @@ class BackgroundManager {
|
|
|
23991
24404
|
}).catch(() => {});
|
|
23992
24405
|
}
|
|
23993
24406
|
const message = `[BACKGROUND TASK COMPLETED] Task "${task.description}" finished in ${duration3}. Use background_output with task_id="${task.id}" to get results.`;
|
|
23994
|
-
|
|
23995
|
-
if (!mainSessionID2) {
|
|
23996
|
-
log("[background-agent] No main session ID available, relying on pending queue");
|
|
23997
|
-
return;
|
|
23998
|
-
}
|
|
23999
|
-
log("[background-agent] Sending notification to main session:", mainSessionID2);
|
|
24407
|
+
log("[background-agent] Sending notification to parent session:", { parentSessionID: task.parentSessionID });
|
|
24000
24408
|
setTimeout(async () => {
|
|
24001
24409
|
try {
|
|
24002
24410
|
await this.client.session.prompt({
|
|
24003
|
-
path: { id:
|
|
24411
|
+
path: { id: task.parentSessionID },
|
|
24004
24412
|
body: {
|
|
24005
24413
|
parts: [{ type: "text", text: message }]
|
|
24006
24414
|
},
|
|
24007
24415
|
query: { directory: this.directory }
|
|
24008
24416
|
});
|
|
24009
24417
|
this.clearNotificationsForTask(task.id);
|
|
24010
|
-
log("[background-agent] Successfully sent prompt to
|
|
24418
|
+
log("[background-agent] Successfully sent prompt to parent session:", { parentSessionID: task.parentSessionID });
|
|
24011
24419
|
} catch (error45) {
|
|
24012
24420
|
log("[background-agent] prompt failed:", String(error45));
|
|
24013
24421
|
}
|
|
@@ -24060,6 +24468,7 @@ class BackgroundManager {
|
|
|
24060
24468
|
const assistantMsgs = messages.filter((m) => m.info?.role === "assistant");
|
|
24061
24469
|
let toolCalls = 0;
|
|
24062
24470
|
let lastTool;
|
|
24471
|
+
let lastMessage;
|
|
24063
24472
|
for (const msg of assistantMsgs) {
|
|
24064
24473
|
const parts = msg.parts ?? [];
|
|
24065
24474
|
for (const part of parts) {
|
|
@@ -24067,6 +24476,9 @@ class BackgroundManager {
|
|
|
24067
24476
|
toolCalls++;
|
|
24068
24477
|
lastTool = part.tool || part.name || "unknown";
|
|
24069
24478
|
}
|
|
24479
|
+
if (part.type === "text" && part.text) {
|
|
24480
|
+
lastMessage = part.text;
|
|
24481
|
+
}
|
|
24070
24482
|
}
|
|
24071
24483
|
}
|
|
24072
24484
|
if (!task.progress) {
|
|
@@ -24075,6 +24487,10 @@ class BackgroundManager {
|
|
|
24075
24487
|
task.progress.toolCalls = toolCalls;
|
|
24076
24488
|
task.progress.lastTool = lastTool;
|
|
24077
24489
|
task.progress.lastUpdate = new Date;
|
|
24490
|
+
if (lastMessage) {
|
|
24491
|
+
task.progress.lastMessage = lastMessage;
|
|
24492
|
+
task.progress.lastMessageAt = new Date;
|
|
24493
|
+
}
|
|
24078
24494
|
}
|
|
24079
24495
|
} catch (error45) {
|
|
24080
24496
|
log("[background-agent] Poll error for task:", { taskId: task.id, error: error45 });
|
|
@@ -24137,6 +24553,23 @@ var AgentNameSchema = exports_external.enum([
|
|
|
24137
24553
|
"frontend-ui-ux-engineer",
|
|
24138
24554
|
"document-writer"
|
|
24139
24555
|
]);
|
|
24556
|
+
var HookNameSchema = exports_external.enum([
|
|
24557
|
+
"todo-continuation-enforcer",
|
|
24558
|
+
"context-window-monitor",
|
|
24559
|
+
"session-recovery",
|
|
24560
|
+
"session-notification",
|
|
24561
|
+
"comment-checker",
|
|
24562
|
+
"grep-output-truncator",
|
|
24563
|
+
"directory-agents-injector",
|
|
24564
|
+
"directory-readme-injector",
|
|
24565
|
+
"empty-task-response-detector",
|
|
24566
|
+
"think-mode",
|
|
24567
|
+
"anthropic-auto-compact",
|
|
24568
|
+
"rules-injector",
|
|
24569
|
+
"background-notification",
|
|
24570
|
+
"auto-update-checker",
|
|
24571
|
+
"ultrawork-mode"
|
|
24572
|
+
]);
|
|
24140
24573
|
var AgentOverrideConfigSchema = exports_external.object({
|
|
24141
24574
|
model: exports_external.string().optional(),
|
|
24142
24575
|
temperature: exports_external.number().min(0).max(2).optional(),
|
|
@@ -24167,6 +24600,7 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
24167
24600
|
$schema: exports_external.string().optional(),
|
|
24168
24601
|
disabled_mcps: exports_external.array(McpNameSchema).optional(),
|
|
24169
24602
|
disabled_agents: exports_external.array(AgentNameSchema).optional(),
|
|
24603
|
+
disabled_hooks: exports_external.array(HookNameSchema).optional(),
|
|
24170
24604
|
agents: AgentOverridesSchema.optional(),
|
|
24171
24605
|
claude_code: ClaudeCodeConfigSchema.optional(),
|
|
24172
24606
|
google_auth: exports_external.boolean().optional()
|
|
@@ -24216,6 +24650,12 @@ function mergeConfigs(base, override) {
|
|
|
24216
24650
|
...override.disabled_mcps ?? []
|
|
24217
24651
|
])
|
|
24218
24652
|
],
|
|
24653
|
+
disabled_hooks: [
|
|
24654
|
+
...new Set([
|
|
24655
|
+
...base.disabled_hooks ?? [],
|
|
24656
|
+
...override.disabled_hooks ?? []
|
|
24657
|
+
])
|
|
24658
|
+
],
|
|
24219
24659
|
claude_code: deepMerge(base.claude_code, override.claude_code)
|
|
24220
24660
|
};
|
|
24221
24661
|
}
|
|
@@ -24231,32 +24671,39 @@ function loadPluginConfig(directory) {
|
|
|
24231
24671
|
agents: config3.agents,
|
|
24232
24672
|
disabled_agents: config3.disabled_agents,
|
|
24233
24673
|
disabled_mcps: config3.disabled_mcps,
|
|
24674
|
+
disabled_hooks: config3.disabled_hooks,
|
|
24234
24675
|
claude_code: config3.claude_code
|
|
24235
24676
|
});
|
|
24236
24677
|
return config3;
|
|
24237
24678
|
}
|
|
24238
24679
|
var OhMyOpenCodePlugin = async (ctx) => {
|
|
24239
24680
|
const pluginConfig = loadPluginConfig(ctx.directory);
|
|
24240
|
-
const
|
|
24241
|
-
const
|
|
24242
|
-
const
|
|
24243
|
-
|
|
24244
|
-
sessionRecovery
|
|
24245
|
-
const
|
|
24246
|
-
|
|
24247
|
-
|
|
24248
|
-
|
|
24249
|
-
|
|
24250
|
-
const
|
|
24681
|
+
const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []);
|
|
24682
|
+
const isHookEnabled = (hookName) => !disabledHooks.has(hookName);
|
|
24683
|
+
const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer") ? createTodoContinuationEnforcer(ctx) : null;
|
|
24684
|
+
const contextWindowMonitor = isHookEnabled("context-window-monitor") ? createContextWindowMonitorHook(ctx) : null;
|
|
24685
|
+
const sessionRecovery = isHookEnabled("session-recovery") ? createSessionRecoveryHook(ctx) : null;
|
|
24686
|
+
const sessionNotification = isHookEnabled("session-notification") ? createSessionNotification(ctx) : null;
|
|
24687
|
+
if (sessionRecovery && todoContinuationEnforcer) {
|
|
24688
|
+
sessionRecovery.setOnAbortCallback(todoContinuationEnforcer.markRecovering);
|
|
24689
|
+
sessionRecovery.setOnRecoveryCompleteCallback(todoContinuationEnforcer.markRecoveryComplete);
|
|
24690
|
+
}
|
|
24691
|
+
const commentChecker = isHookEnabled("comment-checker") ? createCommentCheckerHooks() : null;
|
|
24692
|
+
const grepOutputTruncator = isHookEnabled("grep-output-truncator") ? createGrepOutputTruncatorHook(ctx) : null;
|
|
24693
|
+
const directoryAgentsInjector = isHookEnabled("directory-agents-injector") ? createDirectoryAgentsInjectorHook(ctx) : null;
|
|
24694
|
+
const directoryReadmeInjector = isHookEnabled("directory-readme-injector") ? createDirectoryReadmeInjectorHook(ctx) : null;
|
|
24695
|
+
const emptyTaskResponseDetector = isHookEnabled("empty-task-response-detector") ? createEmptyTaskResponseDetectorHook(ctx) : null;
|
|
24696
|
+
const thinkMode = isHookEnabled("think-mode") ? createThinkModeHook() : null;
|
|
24251
24697
|
const claudeCodeHooks = createClaudeCodeHooksHook(ctx, {
|
|
24252
24698
|
disabledHooks: pluginConfig.claude_code?.hooks ?? true ? undefined : true
|
|
24253
24699
|
});
|
|
24254
|
-
const anthropicAutoCompact = createAnthropicAutoCompactHook(ctx);
|
|
24255
|
-
const rulesInjector = createRulesInjectorHook(ctx);
|
|
24256
|
-
const autoUpdateChecker = createAutoUpdateCheckerHook(ctx);
|
|
24700
|
+
const anthropicAutoCompact = isHookEnabled("anthropic-auto-compact") ? createAnthropicAutoCompactHook(ctx) : null;
|
|
24701
|
+
const rulesInjector = isHookEnabled("rules-injector") ? createRulesInjectorHook(ctx) : null;
|
|
24702
|
+
const autoUpdateChecker = isHookEnabled("auto-update-checker") ? createAutoUpdateCheckerHook(ctx) : null;
|
|
24703
|
+
const ultraworkMode = isHookEnabled("ultrawork-mode") ? createUltraworkModeHook() : null;
|
|
24257
24704
|
updateTerminalTitle({ sessionId: "main" });
|
|
24258
24705
|
const backgroundManager = new BackgroundManager(ctx);
|
|
24259
|
-
const backgroundNotificationHook = createBackgroundNotificationHook(backgroundManager);
|
|
24706
|
+
const backgroundNotificationHook = isHookEnabled("background-notification") ? createBackgroundNotificationHook(backgroundManager) : null;
|
|
24260
24707
|
const backgroundTools = createBackgroundTools(backgroundManager, ctx.client);
|
|
24261
24708
|
const callOmoAgent = createCallOmoAgent(ctx, backgroundManager);
|
|
24262
24709
|
const googleAuthHooks = pluginConfig.google_auth ? await createGoogleAntigravityAuthPlugin(ctx) : null;
|
|
@@ -24269,6 +24716,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
24269
24716
|
},
|
|
24270
24717
|
"chat.message": async (input, output) => {
|
|
24271
24718
|
await claudeCodeHooks["chat.message"]?.(input, output);
|
|
24719
|
+
await ultraworkMode?.["chat.message"]?.(input, output);
|
|
24272
24720
|
},
|
|
24273
24721
|
config: async (config3) => {
|
|
24274
24722
|
const builtinAgents = createBuiltinAgents(pluginConfig.disabled_agents, pluginConfig.agents);
|
|
@@ -24319,16 +24767,18 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
24319
24767
|
};
|
|
24320
24768
|
},
|
|
24321
24769
|
event: async (input) => {
|
|
24322
|
-
await autoUpdateChecker
|
|
24770
|
+
await autoUpdateChecker?.event(input);
|
|
24323
24771
|
await claudeCodeHooks.event(input);
|
|
24324
|
-
await backgroundNotificationHook
|
|
24325
|
-
await
|
|
24326
|
-
await
|
|
24327
|
-
await
|
|
24328
|
-
await
|
|
24329
|
-
await
|
|
24330
|
-
await
|
|
24331
|
-
await
|
|
24772
|
+
await backgroundNotificationHook?.event(input);
|
|
24773
|
+
await sessionNotification?.(input);
|
|
24774
|
+
await todoContinuationEnforcer?.handler(input);
|
|
24775
|
+
await contextWindowMonitor?.event(input);
|
|
24776
|
+
await directoryAgentsInjector?.event(input);
|
|
24777
|
+
await directoryReadmeInjector?.event(input);
|
|
24778
|
+
await rulesInjector?.event(input);
|
|
24779
|
+
await thinkMode?.event(input);
|
|
24780
|
+
await anthropicAutoCompact?.event(input);
|
|
24781
|
+
await ultraworkMode?.event(input);
|
|
24332
24782
|
const { event } = input;
|
|
24333
24783
|
const props = event.properties;
|
|
24334
24784
|
if (event.type === "session.created") {
|
|
@@ -24370,7 +24820,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
24370
24820
|
if (event.type === "session.error") {
|
|
24371
24821
|
const sessionID = props?.sessionID;
|
|
24372
24822
|
const error45 = props?.error;
|
|
24373
|
-
if (sessionRecovery
|
|
24823
|
+
if (sessionRecovery?.isRecoverableError(error45)) {
|
|
24374
24824
|
const messageInfo = {
|
|
24375
24825
|
id: props?.messageID,
|
|
24376
24826
|
role: "assistant",
|
|
@@ -24409,7 +24859,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
24409
24859
|
},
|
|
24410
24860
|
"tool.execute.before": async (input, output) => {
|
|
24411
24861
|
await claudeCodeHooks["tool.execute.before"](input, output);
|
|
24412
|
-
await commentChecker["tool.execute.before"](input, output);
|
|
24862
|
+
await commentChecker?.["tool.execute.before"](input, output);
|
|
24413
24863
|
if (input.sessionID === getMainSessionID()) {
|
|
24414
24864
|
updateTerminalTitle({
|
|
24415
24865
|
sessionId: input.sessionID,
|
|
@@ -24422,13 +24872,13 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
24422
24872
|
},
|
|
24423
24873
|
"tool.execute.after": async (input, output) => {
|
|
24424
24874
|
await claudeCodeHooks["tool.execute.after"](input, output);
|
|
24425
|
-
await grepOutputTruncator["tool.execute.after"](input, output);
|
|
24426
|
-
await contextWindowMonitor["tool.execute.after"](input, output);
|
|
24427
|
-
await commentChecker["tool.execute.after"](input, output);
|
|
24428
|
-
await directoryAgentsInjector["tool.execute.after"](input, output);
|
|
24429
|
-
await directoryReadmeInjector["tool.execute.after"](input, output);
|
|
24430
|
-
await rulesInjector["tool.execute.after"](input, output);
|
|
24431
|
-
await emptyTaskResponseDetector["tool.execute.after"](input, output);
|
|
24875
|
+
await grepOutputTruncator?.["tool.execute.after"](input, output);
|
|
24876
|
+
await contextWindowMonitor?.["tool.execute.after"](input, output);
|
|
24877
|
+
await commentChecker?.["tool.execute.after"](input, output);
|
|
24878
|
+
await directoryAgentsInjector?.["tool.execute.after"](input, output);
|
|
24879
|
+
await directoryReadmeInjector?.["tool.execute.after"](input, output);
|
|
24880
|
+
await rulesInjector?.["tool.execute.after"](input, output);
|
|
24881
|
+
await emptyTaskResponseDetector?.["tool.execute.after"](input, output);
|
|
24432
24882
|
if (input.sessionID === getMainSessionID()) {
|
|
24433
24883
|
updateTerminalTitle({
|
|
24434
24884
|
sessionId: input.sessionID,
|