diff --git a/src/config/schema.ts b/src/config/schema.ts index 5692b65..40fb348 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -39,6 +39,7 @@ export const HookNameSchema = z.enum([ "rules-injector", "background-notification", "auto-update-checker", + "ultrawork-mode", ]) export const AgentOverrideConfigSchema = z.object({ diff --git a/src/hooks/index.ts b/src/hooks/index.ts index be1fcef..12666d7 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -13,3 +13,4 @@ export { createClaudeCodeHooksHook } from "./claude-code-hooks"; export { createRulesInjectorHook } from "./rules-injector"; export { createBackgroundNotificationHook } from "./background-notification" export { createAutoUpdateCheckerHook } from "./auto-update-checker"; +export { createUltraworkModeHook } from "./ultrawork-mode"; diff --git a/src/hooks/ultrawork-mode/constants.ts b/src/hooks/ultrawork-mode/constants.ts new file mode 100644 index 0000000..4341346 --- /dev/null +++ b/src/hooks/ultrawork-mode/constants.ts @@ -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 = ` +[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 + + + +--- + +` diff --git a/src/hooks/ultrawork-mode/detector.ts b/src/hooks/ultrawork-mode/detector.ts new file mode 100644 index 0000000..898c480 --- /dev/null +++ b/src/hooks/ultrawork-mode/detector.ts @@ -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("") +} diff --git a/src/hooks/ultrawork-mode/index.ts b/src/hooks/ultrawork-mode/index.ts new file mode 100644 index 0000000..312ba7f --- /dev/null +++ b/src/hooks/ultrawork-mode/index.ts @@ -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() + +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 + parts: Array<{ type: string; text?: string; [key: string]: unknown }> + } + ): Promise => { + 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) + } + } + }, + } +} diff --git a/src/hooks/ultrawork-mode/types.ts b/src/hooks/ultrawork-mode/types.ts new file mode 100644 index 0000000..6c92ace --- /dev/null +++ b/src/hooks/ultrawork-mode/types.ts @@ -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 +} diff --git a/src/index.ts b/src/index.ts index ba4dca5..84d2207 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,7 @@ import { createRulesInjectorHook, createBackgroundNotificationHook, createAutoUpdateCheckerHook, + createUltraworkModeHook, } from "./hooks"; import { createGoogleAntigravityAuthPlugin } from "./auth/antigravity"; import { @@ -203,6 +204,9 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => { const autoUpdateChecker = isHookEnabled("auto-update-checker") ? createAutoUpdateCheckerHook(ctx) : null; + const ultraworkMode = isHookEnabled("ultrawork-mode") + ? createUltraworkModeHook() + : null; updateTerminalTitle({ sessionId: "main" }); @@ -230,6 +234,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => { "chat.message": async (input, output) => { await claudeCodeHooks["chat.message"]?.(input, output); + await ultraworkMode?.["chat.message"]?.(input, output); }, config: async (config) => { @@ -304,6 +309,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => { await rulesInjector?.event(input); await thinkMode?.event(input); await anthropicAutoCompact?.event(input); + await ultraworkMode?.event(input); const { event } = input; const props = event.properties as Record | undefined;