Files
oh-my-opencode/src/hooks/keyword-detector/constants.ts
T
YeonGyu-Kim 5c67482038 feat(keyword-detector): add hyperplan-ultrawork combo + tuple refactor
Adjacent "hpp ulw" or "ulw hpp" (8 form combinations: short/long, both
orders) triggers a fused mode that suppresses the standalone ultrawork
and hyperplan banners and toasts in favor of one combo banner. The
combo banner explicitly preserves hyperplan's mandatory adversarial
workflow contract (do NOT improvise, do NOT skip rounds) instead of
silently downgrading it.

Suppression runs as a named pipeline step (suppressComboStandalones)
immediately after detection and before all consumers (planner filter,
session filters, toasts, message injection), so standalone toast checks
naturally see the already-suppressed list. Combo is allowed in non-main
sessions like ultrawork, filtered for planner agents like both
standalones, and blocked in subagent sessions via the existing gate.

disabled_keywords uses the intersection rule: disabling either
"ultrawork" or "hyperplan" also disables the combo, so no flavored
content leaks via the combo embedding when either base keyword is
disabled.

Includes a same-PR refactor of KEYWORD_DETECTORS from {pattern, message}
to {type, pattern, message} tuple shape, dropping the parallel hardcoded
types array in detector.ts that previously coupled type assignment to
registry index. Future detector additions can no longer silently corrupt
DetectedKeyword.type via reorder or insertion.

10 behavioral contract tests in hyperplan-ultrawork.test.ts cover both
trigger orders, non-adjacent rejection, suppression of injection and
toast, intersection-rule disable behavior, session/agent policy, and
ultrawork variant routing through the combo. The pre-existing combined
"ultrawork hyperplan" assertion in hyperplan.test.ts is removed in
favor of the new file.

Plan distilled from a hyperplan adversarial review (5 members,
3 rounds: skeptic, validator, researcher, architect, creative).
2026-05-06 14:20:50 +09:00

83 lines
3.9 KiB
TypeScript

export const CODE_BLOCK_PATTERN = /```[\s\S]*?```/g
export const INLINE_CODE_PATTERN = /`[^`]+`/g
export { isPlannerAgent, isNonOmoAgent, getUltraworkMessage } from "./ultrawork"
export { SEARCH_PATTERN, SEARCH_MESSAGE } from "./search"
export { ANALYZE_PATTERN, ANALYZE_MESSAGE } from "./analyze"
export { TEAM_PATTERN, TEAM_MESSAGE } from "./team"
export { HYPERPLAN_PATTERN, HYPERPLAN_MESSAGE } from "./hyperplan"
import type { KeywordType } from "../../config/schema/keyword-detector"
import { getUltraworkMessage } from "./ultrawork"
import { SEARCH_PATTERN, SEARCH_MESSAGE } from "./search"
import { TEAM_PATTERN, TEAM_MESSAGE } from "./team"
import { HYPERPLAN_PATTERN, HYPERPLAN_MESSAGE } from "./hyperplan"
// Hyperplan-ultrawork combo: strict adjacency, both word orders
export const HYPERPLAN_ULTRAWORK_PATTERN =
/\b(?:hpp|hyperplan)\s+(?:ulw|ultrawork)\b|\b(?:ulw|ultrawork)\s+(?:hpp|hyperplan)\b/i
const HYPERPLAN_ULTRAWORK_BANNER = `<hyperplan-ultrawork-mode>
**MANDATORY**: Say "HYPERPLAN ULTRAWORK MODE ENABLED!" exactly once as your first response. Do NOT say the standalone "ULTRAWORK MODE ENABLED!" or "HYPERPLAN MODE ENABLED!" banners.
Apply the ultrawork protocol below as your execution framework. You MUST ALSO load the hyperplan skill immediately via \`skill(name="hyperplan")\` and follow its full adversarial workflow — do NOT improvise, do NOT skip rounds, do NOT write the plan yourself.
</hyperplan-ultrawork-mode>`
export function getHyperplanUltraworkMessage(agentName?: string, modelID?: string): string {
return `${HYPERPLAN_ULTRAWORK_BANNER}\n\n${getUltraworkMessage(agentName, modelID)}`
}
export type KeywordDetector = {
type: KeywordType
pattern: RegExp
message: string | ((agentName?: string, modelID?: string) => string)
}
export const KEYWORD_DETECTORS: KeywordDetector[] = [
{
type: "ultrawork",
pattern: /\b(ultrawork|ulw)\b/i,
message: getUltraworkMessage,
},
{
type: "search",
pattern: SEARCH_PATTERN,
message: SEARCH_MESSAGE,
},
{
type: "analyze",
pattern:
/\b(analyze|analyse|investigate|examine|research|study|deep[\s-]?dive|inspect|audit|evaluate|assess|review|diagnose|scrutinize|dissect|debug|comprehend|interpret|breakdown|understand)\b|why\s+is|how\s+does|how\s+to|분석|조사|파악|연구|검토|진단|이해|설명|원인|이유|뜯어봐|따져봐|평가|해석|디버깅|디버그|어떻게|왜|살펴|分析|調査|解析|検討|研究|診断|理解|説明|検証|精査|究明|デバッグ|なぜ|どう|仕組み|调查|检查|剖析|深入|诊断|解释|调试|为什么|原理|搞清楚|弄明白|phân tích|điều tra|nghiên cứu|kiểm tra|xem xét|chẩn đoán|giải thích|tìm hiểu|gỡ lỗi|tại sao/i,
message: `[analyze-mode]
ANALYSIS MODE. Gather context before diving deep:
CONTEXT GATHERING (parallel):
- 1-2 explore agents (codebase patterns, implementations)
- 1-2 librarian agents (if external library involved)
- Direct tools: Grep, AST-grep, LSP for targeted searches
IF COMPLEX - DO NOT STRUGGLE ALONE. Consult specialists:
- **Oracle**: Conventional problems (architecture, debugging, complex logic)
- **Artistry**: Non-conventional problems (different approach needed)
SYNTHESIZE findings before proceeding.
---
MANDATORY delegate_task params: ALWAYS include load_skills and run_in_background when calling delegate_task. Evaluate available skills before dispatch - pass task-appropriate skills when relevant, pass [] ONLY when no skill matches the task domain.
Example: delegate_task(subagent_type="explore", prompt="...", run_in_background=true, load_skills=[])`,
},
{
type: "team",
pattern: TEAM_PATTERN,
message: TEAM_MESSAGE,
},
{
type: "hyperplan",
pattern: HYPERPLAN_PATTERN,
message: HYPERPLAN_MESSAGE,
},
{
type: "hyperplan-ultrawork",
pattern: HYPERPLAN_ULTRAWORK_PATTERN,
message: getHyperplanUltraworkMessage,
},
]