feat(keyword-detector): add hyperplan keyword pattern, message and test
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,244 @@
|
|||||||
|
import { describe, expect, test, beforeEach, afterEach, spyOn } from "bun:test"
|
||||||
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
|
import { createKeywordDetectorHook } from "./index"
|
||||||
|
import { setMainSession, _resetForTesting } from "../../features/claude-code-session-state"
|
||||||
|
import * as sharedModule from "../../shared"
|
||||||
|
import * as sessionState from "../../features/claude-code-session-state"
|
||||||
|
|
||||||
|
describe("keyword-detector hyperplan keyword", () => {
|
||||||
|
let logSpy: ReturnType<typeof spyOn>
|
||||||
|
let getMainSessionSpy: ReturnType<typeof spyOn>
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
_resetForTesting()
|
||||||
|
logSpy = spyOn(sharedModule, "log").mockImplementation(() => {})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
logSpy?.mockRestore()
|
||||||
|
getMainSessionSpy?.mockRestore()
|
||||||
|
_resetForTesting()
|
||||||
|
})
|
||||||
|
|
||||||
|
function createMockPluginInput(options: { toastCalls?: string[] } = {}) {
|
||||||
|
const toastCalls = options.toastCalls ?? []
|
||||||
|
return {
|
||||||
|
client: {
|
||||||
|
tui: {
|
||||||
|
showToast: async (opts: { body: { title: string } }) => {
|
||||||
|
toastCalls.push(opts.body.title)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as unknown as PluginInput
|
||||||
|
}
|
||||||
|
|
||||||
|
test("should inject hyperplan message when user types 'hyperplan'", async () => {
|
||||||
|
// given - main session typing the full keyword
|
||||||
|
const sessionID = "hyperplan-full-session"
|
||||||
|
getMainSessionSpy = spyOn(sessionState, "getMainSessionID").mockReturnValue(sessionID)
|
||||||
|
const hook = createKeywordDetectorHook(createMockPluginInput())
|
||||||
|
const output = {
|
||||||
|
message: {} as Record<string, unknown>,
|
||||||
|
parts: [{ type: "text", text: "hyperplan refactor the auth module" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when - keyword detection runs
|
||||||
|
await hook["chat.message"]({ sessionID }, output)
|
||||||
|
|
||||||
|
// then - hyperplan-mode wrapper and skill-loading instruction should be present
|
||||||
|
const textPart = output.parts.find(p => p.type === "text")
|
||||||
|
expect(textPart).toBeDefined()
|
||||||
|
expect(textPart!.text).toContain("<hyperplan-mode>")
|
||||||
|
expect(textPart!.text).toContain('skill(name="hyperplan")')
|
||||||
|
expect(textPart!.text).toContain("HYPERPLAN MODE ENABLED")
|
||||||
|
expect(textPart!.text).toContain("refactor the auth module")
|
||||||
|
expect(textPart!.text).toContain("---")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should inject hyperplan message when user types 'hpp' shorthand", async () => {
|
||||||
|
// given - main session typing the short keyword
|
||||||
|
const sessionID = "hyperplan-short-session"
|
||||||
|
getMainSessionSpy = spyOn(sessionState, "getMainSessionID").mockReturnValue(sessionID)
|
||||||
|
const hook = createKeywordDetectorHook(createMockPluginInput())
|
||||||
|
const output = {
|
||||||
|
message: {} as Record<string, unknown>,
|
||||||
|
parts: [{ type: "text", text: "hpp how should I structure this feature" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when - keyword detection runs
|
||||||
|
await hook["chat.message"]({ sessionID }, output)
|
||||||
|
|
||||||
|
// then - hyperplan injection should fire
|
||||||
|
const textPart = output.parts.find(p => p.type === "text")
|
||||||
|
expect(textPart).toBeDefined()
|
||||||
|
expect(textPart!.text).toContain("<hyperplan-mode>")
|
||||||
|
expect(textPart!.text).toContain('skill(name="hyperplan")')
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should inject hyperplan message case-insensitively", async () => {
|
||||||
|
// given - main session typing in mixed case
|
||||||
|
const sessionID = "hyperplan-case-session"
|
||||||
|
getMainSessionSpy = spyOn(sessionState, "getMainSessionID").mockReturnValue(sessionID)
|
||||||
|
const hook = createKeywordDetectorHook(createMockPluginInput())
|
||||||
|
const output = {
|
||||||
|
message: {} as Record<string, unknown>,
|
||||||
|
parts: [{ type: "text", text: "HyperPlan something now" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when - keyword detection runs with mixed case input
|
||||||
|
await hook["chat.message"]({ sessionID }, output)
|
||||||
|
|
||||||
|
// then - hyperplan should still fire
|
||||||
|
const textPart = output.parts.find(p => p.type === "text")
|
||||||
|
expect(textPart).toBeDefined()
|
||||||
|
expect(textPart!.text).toContain("<hyperplan-mode>")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should NOT trigger hyperplan when 'hpp' is a substring of another word", async () => {
|
||||||
|
// given - text contains 'hpp' only as part of larger string with no word boundary
|
||||||
|
const sessionID = "hyperplan-substring-session"
|
||||||
|
getMainSessionSpy = spyOn(sessionState, "getMainSessionID").mockReturnValue(sessionID)
|
||||||
|
const hook = createKeywordDetectorHook(createMockPluginInput())
|
||||||
|
const output = {
|
||||||
|
message: {} as Record<string, unknown>,
|
||||||
|
parts: [{ type: "text", text: "myhppvar = 1" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when - keyword detection runs
|
||||||
|
await hook["chat.message"]({ sessionID }, output)
|
||||||
|
|
||||||
|
// then - hyperplan should NOT trigger because 'hpp' lacks word boundaries
|
||||||
|
const textPart = output.parts.find(p => p.type === "text")
|
||||||
|
expect(textPart).toBeDefined()
|
||||||
|
expect(textPart!.text).toBe("myhppvar = 1")
|
||||||
|
expect(textPart!.text).not.toContain("<hyperplan-mode>")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should fire 'Hyperplan Mode Activated' toast when keyword detected", async () => {
|
||||||
|
// given - main session and toast tracking
|
||||||
|
const sessionID = "hyperplan-toast-session"
|
||||||
|
getMainSessionSpy = spyOn(sessionState, "getMainSessionID").mockReturnValue(sessionID)
|
||||||
|
const toastCalls: string[] = []
|
||||||
|
const hook = createKeywordDetectorHook(createMockPluginInput({ toastCalls }))
|
||||||
|
const output = {
|
||||||
|
message: {} as Record<string, unknown>,
|
||||||
|
parts: [{ type: "text", text: "hyperplan this task" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when - hyperplan keyword fires
|
||||||
|
await hook["chat.message"]({ sessionID }, output)
|
||||||
|
|
||||||
|
// then - toast title should be present in tracked calls
|
||||||
|
expect(toastCalls).toContain("Hyperplan Mode Activated")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should NOT inject hyperplan when disabled_keywords includes 'hyperplan'", async () => {
|
||||||
|
// given - keyword detector with hyperplan disabled
|
||||||
|
const sessionID = "hyperplan-disabled-session"
|
||||||
|
getMainSessionSpy = spyOn(sessionState, "getMainSessionID").mockReturnValue(sessionID)
|
||||||
|
const toastCalls: string[] = []
|
||||||
|
const hook = createKeywordDetectorHook(
|
||||||
|
createMockPluginInput({ toastCalls }),
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{ disabled_keywords: ["hyperplan"] },
|
||||||
|
)
|
||||||
|
const output = {
|
||||||
|
message: {} as Record<string, unknown>,
|
||||||
|
parts: [{ type: "text", text: "hyperplan refactor this" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when - hyperplan keyword would normally fire
|
||||||
|
await hook["chat.message"]({ sessionID }, output)
|
||||||
|
|
||||||
|
// then - neither injection nor toast should occur
|
||||||
|
const textPart = output.parts.find(p => p.type === "text")
|
||||||
|
expect(textPart).toBeDefined()
|
||||||
|
expect(textPart!.text).toBe("hyperplan refactor this")
|
||||||
|
expect(textPart!.text).not.toContain("<hyperplan-mode>")
|
||||||
|
expect(toastCalls).not.toContain("Hyperplan Mode Activated")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should filter hyperplan keyword in non-main session (only ultrawork allowed there)", async () => {
|
||||||
|
// given - main session set, different (subagent) session triggers hyperplan
|
||||||
|
const mainSessionID = "main-hyperplan"
|
||||||
|
const subagentSessionID = "subagent-hyperplan"
|
||||||
|
setMainSession(mainSessionID)
|
||||||
|
const hook = createKeywordDetectorHook(createMockPluginInput())
|
||||||
|
const output = {
|
||||||
|
message: {} as Record<string, unknown>,
|
||||||
|
parts: [{ type: "text", text: "hyperplan please" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when - subagent session triggers hyperplan keyword
|
||||||
|
await hook["chat.message"]({ sessionID: subagentSessionID }, output)
|
||||||
|
|
||||||
|
// then - hyperplan injection should be skipped in non-main session
|
||||||
|
const textPart = output.parts.find(p => p.type === "text")
|
||||||
|
expect(textPart).toBeDefined()
|
||||||
|
expect(textPart!.text).toBe("hyperplan please")
|
||||||
|
expect(textPart!.text).not.toContain("<hyperplan-mode>")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should skip hyperplan injection when agent is prometheus (planner)", async () => {
|
||||||
|
// given - hook running with prometheus agent and a prompt that only triggers hyperplan
|
||||||
|
const sessionID = "hyperplan-prometheus-session"
|
||||||
|
const hook = createKeywordDetectorHook(createMockPluginInput())
|
||||||
|
const output = {
|
||||||
|
message: {} as Record<string, unknown>,
|
||||||
|
parts: [{ type: "text", text: "hyperplan refactor stuff" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when - hyperplan keyword detected with prometheus agent
|
||||||
|
await hook["chat.message"]({ sessionID, agent: "prometheus" }, output)
|
||||||
|
|
||||||
|
// then - hyperplan should be filtered out for planner agents
|
||||||
|
const textPart = output.parts.find(p => p.type === "text")
|
||||||
|
expect(textPart).toBeDefined()
|
||||||
|
expect(textPart!.text).not.toContain("<hyperplan-mode>")
|
||||||
|
expect(textPart!.text).not.toContain('skill(name="hyperplan")')
|
||||||
|
expect(textPart!.text).toContain("hyperplan refactor stuff")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should skip hyperplan injection when agent name contains 'planner' token", async () => {
|
||||||
|
// given - hook running with planner-named agent and a prompt that only triggers hpp
|
||||||
|
const sessionID = "hyperplan-planner-session"
|
||||||
|
const hook = createKeywordDetectorHook(createMockPluginInput())
|
||||||
|
const output = {
|
||||||
|
message: {} as Record<string, unknown>,
|
||||||
|
parts: [{ type: "text", text: "hpp build the feature" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when - hpp keyword detected with planner agent
|
||||||
|
await hook["chat.message"]({ sessionID, agent: "Plan Agent" }, output)
|
||||||
|
|
||||||
|
// then - hyperplan should be filtered out
|
||||||
|
const textPart = output.parts.find(p => p.type === "text")
|
||||||
|
expect(textPart).toBeDefined()
|
||||||
|
expect(textPart!.text).not.toContain("<hyperplan-mode>")
|
||||||
|
expect(textPart!.text).not.toContain('skill(name="hyperplan")')
|
||||||
|
expect(textPart!.text).toContain("hpp build the feature")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should inject hyperplan AND ultrawork together when both keywords present", async () => {
|
||||||
|
// given - main session typing both keywords in the same message
|
||||||
|
const sessionID = "hyperplan-combo-session"
|
||||||
|
getMainSessionSpy = spyOn(sessionState, "getMainSessionID").mockReturnValue(sessionID)
|
||||||
|
const hook = createKeywordDetectorHook(createMockPluginInput())
|
||||||
|
const output = {
|
||||||
|
message: {} as Record<string, unknown>,
|
||||||
|
parts: [{ type: "text", text: "ultrawork hyperplan ship this feature" }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when - both keywords trigger
|
||||||
|
await hook["chat.message"]({ sessionID }, output)
|
||||||
|
|
||||||
|
// then - both messages should be present
|
||||||
|
const textPart = output.parts.find(p => p.type === "text")
|
||||||
|
expect(textPart).toBeDefined()
|
||||||
|
expect(textPart!.text).toContain("<hyperplan-mode>")
|
||||||
|
expect(textPart!.text).toContain("YOU MUST LEVERAGE ALL AVAILABLE AGENTS")
|
||||||
|
expect(textPart!.text).toContain("ship this feature")
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Hyperplan keyword detector.
|
||||||
|
*
|
||||||
|
* Triggers when the user wants adversarial multi-agent planning via team-mode.
|
||||||
|
*
|
||||||
|
* Triggers (case-insensitive, word-bounded):
|
||||||
|
* - English: hyperplan, hpp
|
||||||
|
*
|
||||||
|
* The detector injects a thin wrapper that loads the `hyperplan` skill, which
|
||||||
|
* carries the full orchestration instructions for the 5-member adversarial team.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const HYPERPLAN_PATTERN = /\b(hyperplan|hpp)\b/i
|
||||||
|
|
||||||
|
export const HYPERPLAN_MESSAGE = `<hyperplan-mode>
|
||||||
|
**MANDATORY**: Say "HYPERPLAN MODE ENABLED!" as your first response, exactly once.
|
||||||
|
|
||||||
|
The user invoked **hyperplan mode** — adversarial multi-agent planning via team-mode.
|
||||||
|
|
||||||
|
LOAD THE HYPERPLAN SKILL IMMEDIATELY:
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
skill(name="hyperplan")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
After loading, follow the skill's 6-phase workflow EXACTLY:
|
||||||
|
1. Acknowledge and capture the planning request
|
||||||
|
2. Spawn the 5-member adversarial team via team_create
|
||||||
|
3. Round 1 — Independent analysis (each member produces findings)
|
||||||
|
4. Round 2 — Cross-attack (each member ruthlessly attacks the other 4's findings)
|
||||||
|
5. Round 3 — Defend, refine, or concede
|
||||||
|
6. Synthesize defensible insights into a work plan + clean up the team
|
||||||
|
|
||||||
|
Do NOT improvise. Do NOT skip rounds. Be the lead orchestrator and let the adversarial members do the cross-critique.
|
||||||
|
|
||||||
|
If team-mode is unavailable (\`team_*\` tools missing), instruct the user to set \`team_mode.enabled: true\` in \`~/.config/opencode/oh-my-opencode.jsonc\` and restart opencode.
|
||||||
|
</hyperplan-mode>`
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { HYPERPLAN_PATTERN, HYPERPLAN_MESSAGE } from "./default"
|
||||||
Reference in New Issue
Block a user