feat(hooks): add ultrawork-mode hook for automatic agent orchestration guidance
When "ultrawork" or "ulw" keyword is detected in user prompt: - Injects ULTRAWORK_CONTEXT with agent-agnostic guidance - Executes AFTER CC hooks (UserPromptSubmit etc.) - Follows existing hook pattern (think-mode style) Key features: - Agent orchestration principles (by capability, not name) - Parallel execution rules - TODO tracking enforcement - Delegation guidance Closes #31 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
@@ -39,6 +39,7 @@ export const HookNameSchema = z.enum([
|
|||||||
"rules-injector",
|
"rules-injector",
|
||||||
"background-notification",
|
"background-notification",
|
||||||
"auto-update-checker",
|
"auto-update-checker",
|
||||||
|
"ultrawork-mode",
|
||||||
])
|
])
|
||||||
|
|
||||||
export const AgentOverrideConfigSchema = z.object({
|
export const AgentOverrideConfigSchema = z.object({
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ export { createClaudeCodeHooksHook } from "./claude-code-hooks";
|
|||||||
export { createRulesInjectorHook } from "./rules-injector";
|
export { createRulesInjectorHook } from "./rules-injector";
|
||||||
export { createBackgroundNotificationHook } from "./background-notification"
|
export { createBackgroundNotificationHook } from "./background-notification"
|
||||||
export { createAutoUpdateCheckerHook } from "./auto-update-checker";
|
export { createAutoUpdateCheckerHook } from "./auto-update-checker";
|
||||||
|
export { createUltraworkModeHook } from "./ultrawork-mode";
|
||||||
|
|||||||
48
src/hooks/ultrawork-mode/constants.ts
Normal file
48
src/hooks/ultrawork-mode/constants.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/** Keyword patterns - "ultrawork", "ulw" (case-insensitive, word boundary) */
|
||||||
|
export const ULTRAWORK_PATTERNS = [/\bultrawork\b/i, /\bulw\b/i]
|
||||||
|
|
||||||
|
/** Code block pattern to exclude from keyword detection */
|
||||||
|
export const CODE_BLOCK_PATTERN = /```[\s\S]*?```/g
|
||||||
|
|
||||||
|
/** Inline code pattern to exclude */
|
||||||
|
export const INLINE_CODE_PATTERN = /`[^`]+`/g
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ULTRAWORK_CONTEXT - Agent-Agnostic Guidance
|
||||||
|
*
|
||||||
|
* Key principles:
|
||||||
|
* - NO specific agent names (oracle, librarian, etc.)
|
||||||
|
* - Only provide guidance based on agent role/capability
|
||||||
|
* - Emphasize parallel execution, TODO tracking, delegation
|
||||||
|
*/
|
||||||
|
export const ULTRAWORK_CONTEXT = `<ultrawork-mode>
|
||||||
|
[CODE RED] Maximum precision required. Ultrathink before acting.
|
||||||
|
|
||||||
|
YOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL.
|
||||||
|
TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
|
||||||
|
|
||||||
|
## AGENT UTILIZATION PRINCIPLES (by capability, not by name)
|
||||||
|
- **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS for file patterns, internal implementations, project structure
|
||||||
|
- **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS for API references, examples, external library docs
|
||||||
|
- **Planning & Strategy**: NEVER plan yourself - ALWAYS spawn a dedicated planning agent for work breakdown
|
||||||
|
- **High-IQ Reasoning**: Leverage specialized agents for architecture decisions, code review, strategic planning
|
||||||
|
- **Frontend/UI Tasks**: Delegate to UI-specialized agents for design and implementation
|
||||||
|
|
||||||
|
## EXECUTION RULES
|
||||||
|
- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each.
|
||||||
|
- **PARALLEL**: Fire independent agent calls simultaneously via background_task - NEVER wait sequentially.
|
||||||
|
- **BACKGROUND FIRST**: Use background_task for exploration/research agents (10+ concurrent if needed).
|
||||||
|
- **VERIFY**: Re-read request after completion. Check ALL requirements met before reporting done.
|
||||||
|
- **DELEGATE**: Don't do everything yourself - orchestrate specialized agents for their strengths.
|
||||||
|
|
||||||
|
## WORKFLOW
|
||||||
|
1. Analyze the request and identify required capabilities
|
||||||
|
2. Spawn exploration/librarian agents via background_task in PARALLEL (10+ if needed)
|
||||||
|
3. Use planning agents to create detailed work breakdown
|
||||||
|
4. Execute with continuous verification against original requirements
|
||||||
|
|
||||||
|
</ultrawork-mode>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`
|
||||||
33
src/hooks/ultrawork-mode/detector.ts
Normal file
33
src/hooks/ultrawork-mode/detector.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import {
|
||||||
|
ULTRAWORK_PATTERNS,
|
||||||
|
CODE_BLOCK_PATTERN,
|
||||||
|
INLINE_CODE_PATTERN,
|
||||||
|
} from "./constants"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove code blocks and inline code from text.
|
||||||
|
* Prevents false positives when keywords appear in code.
|
||||||
|
*/
|
||||||
|
export function removeCodeBlocks(text: string): string {
|
||||||
|
return text.replace(CODE_BLOCK_PATTERN, "").replace(INLINE_CODE_PATTERN, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect ultrawork keywords in text (excluding code blocks).
|
||||||
|
*/
|
||||||
|
export function detectUltraworkKeyword(text: string): boolean {
|
||||||
|
const textWithoutCode = removeCodeBlocks(text)
|
||||||
|
return ULTRAWORK_PATTERNS.some((pattern) => pattern.test(textWithoutCode))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract text content from message parts.
|
||||||
|
*/
|
||||||
|
export function extractPromptText(
|
||||||
|
parts: Array<{ type: string; text?: string }>
|
||||||
|
): string {
|
||||||
|
return parts
|
||||||
|
.filter((p) => p.type === "text")
|
||||||
|
.map((p) => p.text || "")
|
||||||
|
.join("")
|
||||||
|
}
|
||||||
84
src/hooks/ultrawork-mode/index.ts
Normal file
84
src/hooks/ultrawork-mode/index.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { detectUltraworkKeyword, extractPromptText } from "./detector"
|
||||||
|
import { ULTRAWORK_CONTEXT } from "./constants"
|
||||||
|
import type { UltraworkModeState } from "./types"
|
||||||
|
import { log } from "../../shared"
|
||||||
|
|
||||||
|
export * from "./detector"
|
||||||
|
export * from "./constants"
|
||||||
|
export * from "./types"
|
||||||
|
|
||||||
|
const ultraworkModeState = new Map<string, UltraworkModeState>()
|
||||||
|
|
||||||
|
export function clearUltraworkModeState(sessionID: string): void {
|
||||||
|
ultraworkModeState.delete(sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createUltraworkModeHook() {
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* chat.message hook - detect ultrawork/ulw keywords, inject context
|
||||||
|
*
|
||||||
|
* Execution timing: AFTER claudeCodeHooks["chat.message"]
|
||||||
|
* Behavior:
|
||||||
|
* 1. Extract text from user prompt
|
||||||
|
* 2. Detect ultrawork/ulw keywords (excluding code blocks)
|
||||||
|
* 3. If detected, prepend ULTRAWORK_CONTEXT to first text part
|
||||||
|
*/
|
||||||
|
"chat.message": async (
|
||||||
|
input: {
|
||||||
|
sessionID: string
|
||||||
|
agent?: string
|
||||||
|
model?: { providerID: string; modelID: string }
|
||||||
|
messageID?: string
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
message: Record<string, unknown>
|
||||||
|
parts: Array<{ type: string; text?: string; [key: string]: unknown }>
|
||||||
|
}
|
||||||
|
): Promise<void> => {
|
||||||
|
const state: UltraworkModeState = {
|
||||||
|
detected: false,
|
||||||
|
injected: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
const promptText = extractPromptText(output.parts)
|
||||||
|
|
||||||
|
if (!detectUltraworkKeyword(promptText)) {
|
||||||
|
ultraworkModeState.set(input.sessionID, state)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.detected = true
|
||||||
|
log("Ultrawork keyword detected", { sessionID: input.sessionID })
|
||||||
|
|
||||||
|
const parts = output.parts as Array<{ type: string; text?: string }>
|
||||||
|
const idx = parts.findIndex((p) => p.type === "text" && p.text)
|
||||||
|
|
||||||
|
if (idx >= 0) {
|
||||||
|
parts[idx].text = `${ULTRAWORK_CONTEXT}${parts[idx].text ?? ""}`
|
||||||
|
state.injected = true
|
||||||
|
log("Ultrawork context injected", { sessionID: input.sessionID })
|
||||||
|
}
|
||||||
|
|
||||||
|
ultraworkModeState.set(input.sessionID, state)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event hook - cleanup session state on deletion
|
||||||
|
*/
|
||||||
|
event: async ({
|
||||||
|
event,
|
||||||
|
}: {
|
||||||
|
event: { type: string; properties?: unknown }
|
||||||
|
}) => {
|
||||||
|
if (event.type === "session.deleted") {
|
||||||
|
const props = event.properties as
|
||||||
|
| { info?: { id?: string } }
|
||||||
|
| undefined
|
||||||
|
if (props?.info?.id) {
|
||||||
|
ultraworkModeState.delete(props.info.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/hooks/ultrawork-mode/types.ts
Normal file
20
src/hooks/ultrawork-mode/types.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
export interface UltraworkModeState {
|
||||||
|
/** Whether ultrawork keyword was detected */
|
||||||
|
detected: boolean
|
||||||
|
/** Whether context was injected */
|
||||||
|
injected: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModelRef {
|
||||||
|
providerID: string
|
||||||
|
modelID: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageWithModel {
|
||||||
|
model?: ModelRef
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UltraworkModeInput {
|
||||||
|
parts: Array<{ type: string; text?: string }>
|
||||||
|
message: MessageWithModel
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
createRulesInjectorHook,
|
createRulesInjectorHook,
|
||||||
createBackgroundNotificationHook,
|
createBackgroundNotificationHook,
|
||||||
createAutoUpdateCheckerHook,
|
createAutoUpdateCheckerHook,
|
||||||
|
createUltraworkModeHook,
|
||||||
} from "./hooks";
|
} from "./hooks";
|
||||||
import { createGoogleAntigravityAuthPlugin } from "./auth/antigravity";
|
import { createGoogleAntigravityAuthPlugin } from "./auth/antigravity";
|
||||||
import {
|
import {
|
||||||
@@ -203,6 +204,9 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
const autoUpdateChecker = isHookEnabled("auto-update-checker")
|
const autoUpdateChecker = isHookEnabled("auto-update-checker")
|
||||||
? createAutoUpdateCheckerHook(ctx)
|
? createAutoUpdateCheckerHook(ctx)
|
||||||
: null;
|
: null;
|
||||||
|
const ultraworkMode = isHookEnabled("ultrawork-mode")
|
||||||
|
? createUltraworkModeHook()
|
||||||
|
: null;
|
||||||
|
|
||||||
updateTerminalTitle({ sessionId: "main" });
|
updateTerminalTitle({ sessionId: "main" });
|
||||||
|
|
||||||
@@ -230,6 +234,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
|
|
||||||
"chat.message": async (input, output) => {
|
"chat.message": async (input, output) => {
|
||||||
await claudeCodeHooks["chat.message"]?.(input, output);
|
await claudeCodeHooks["chat.message"]?.(input, output);
|
||||||
|
await ultraworkMode?.["chat.message"]?.(input, output);
|
||||||
},
|
},
|
||||||
|
|
||||||
config: async (config) => {
|
config: async (config) => {
|
||||||
@@ -304,6 +309,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
await rulesInjector?.event(input);
|
await rulesInjector?.event(input);
|
||||||
await thinkMode?.event(input);
|
await thinkMode?.event(input);
|
||||||
await anthropicAutoCompact?.event(input);
|
await anthropicAutoCompact?.event(input);
|
||||||
|
await ultraworkMode?.event(input);
|
||||||
|
|
||||||
const { event } = input;
|
const { event } = input;
|
||||||
const props = event.properties as Record<string, unknown> | undefined;
|
const props = event.properties as Record<string, unknown> | undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user