From 87e28d26071a397dd799ecceb8bdf7dd841517c0 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 24 May 2026 19:16:02 +0900 Subject: [PATCH 1/5] docs(prometheus): rewrite AGENTS.md for thin-loader structure Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/agents/prometheus/AGENTS.md | 45 +++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/agents/prometheus/AGENTS.md b/src/agents/prometheus/AGENTS.md index 3eabbb0b8..ad40e95d6 100644 --- a/src/agents/prometheus/AGENTS.md +++ b/src/agents/prometheus/AGENTS.md @@ -1,30 +1,47 @@ --- name: prometheus-agent -description: Developer reference for the Prometheus strategic planner agent — interview flow, plan output format, and key constraints. +description: Developer reference for the Prometheus strategic planner agent prompt loaders, prompts-core markdown variants, and model routing. --- # src/agents/prometheus/ -- Strategic Planner -**Generated:** 2026-05-15 +**Generated:** 2026-05-24 ## OVERVIEW -11 files. Prometheus agent -- interview-mode strategic planner. Reads codebase, questions user, builds detailed work plan before any code is written. Markdown-only output (enforced by `prometheus-md-only` hook). +5 TypeScript files plus 3 markdown prompt variants in [`packages/prompts-core/prompts/prometheus/`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/prometheus/). Prometheus remains the interview-mode strategic planner, but this directory is now a thin adapter layer. Prompt content lives in `packages/prompts-core`; `src/agents/prometheus/` only loads the right markdown variant and applies runtime tool gating. + +This shape follows the package layering refactor in [`ROADMAP.md`](file:///Users/yeongyu/local-workspaces/omo/ROADMAP.md): prompts are harness-neutral core assets, while the OpenCode adapter keeps only model routing and runtime integration. ## FILES | File | Purpose | |------|---------| -| `system-prompt.ts` | Composes full system prompt from sections | -| `identity-constraints.ts` | FORBIDDEN actions, .md-only enforcement, path restrictions | -| `interview-mode.ts` | Interview flow: gather requirements, clarify scope | -| `plan-generation.ts` | Plan output structure and validation | -| `plan-template.ts` | YAML plan template with task graph, dependencies, waves | -| `behavioral-summary.ts` | Behavioral guidelines section | -| `high-accuracy-mode.ts` | Enhanced accuracy mode for complex plans | -| `gemini.ts` | Gemini-optimized prompt variant | -| `gpt.ts` | GPT-optimized prompt variant | | `index.ts` | Barrel exports | +| `system-prompt.ts` | Thin loader using `loadPromptSync()` and `prometheusPromptVariants` from `@oh-my-opencode/prompts-core`; exports prompt source routing and disabled-tool filtering | +| `gpt.ts` | Thin loader for `PROMETHEUS_GPT_SYSTEM_PROMPT` from `packages/prompts-core/prompts/prometheus/gpt.md` | +| `gemini.ts` | Thin loader for `PROMETHEUS_GEMINI_SYSTEM_PROMPT` from `packages/prompts-core/prompts/prometheus/gemini.md` | +| `system-prompt.test.ts` | Runtime behavior tests for Question tool filtering | +| `prometheus-byte-exactness.test.ts` | Byte-exact sha256 characterization tests for all variants and Question disabled state | +| `packages/prompts-core/prompts/prometheus/default.md` | Default/Claude markdown prompt variant | +| `packages/prompts-core/prompts/prometheus/gpt.md` | GPT-optimized markdown prompt variant | +| `packages/prompts-core/prompts/prometheus/gemini.md` | Gemini-optimized markdown prompt variant | + +## MODEL VARIANT ROUTING + +[`system-prompt.ts`](file:///Users/yeongyu/local-workspaces/omo/src/agents/prometheus/system-prompt.ts) exposes `getPrometheusPromptSource(model)`: + +- GPT family models, as detected by `isGptModel(model)`, route to `"gpt"`. +- Gemini family models, as detected by `isGeminiModel(model)`, route to `"gemini"`. +- Missing models and all other families route to `"default"`. + +`getPrometheusPrompt(model, disabledTools)` then loads the selected markdown through `loadPromptSync({ source: prometheusPromptVariants[variant], name: "prometheus", variant })` and returns the loaded body. + +## DISABLED TOOL HANDLING + +Prometheus normally includes `Question({ ... })` examples because interview mode uses the Question tool to clarify scope. When the runtime passes `disabledTools` containing `"question"`, `getPrometheusPrompt()` strips fenced TypeScript `Question({ ... })` examples with `QUESTION_TOOL_BLOCK_RE` before returning the prompt. + +This filtering is runtime adapter behavior. Do not duplicate stripped markdown variants in `packages/prompts-core`; keep one source of truth per model family and let `system-prompt.ts` remove Question examples only when the tool is disabled. ## KEY CONSTRAINTS @@ -33,10 +50,12 @@ description: Developer reference for the Prometheus strategic planner agent — - Must explore codebase before planning (NEVER plan blind) - Plans saved to `.omo/plans/` - Acceptance criteria requiring "user manually tests" are FORBIDDEN +- Prompt edits belong in [`packages/prompts-core/prompts/prometheus/`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/prometheus/), not in TypeScript section files ## PLAN OUTPUT FORMAT -Plans use YAML with parallel task graph: +The markdown variants instruct Prometheus to produce YAML plans with a parallel task graph: + - Waves (parallel execution groups) - Tasks with dependencies, category, skills - Each task has atomic scope + verification criteria From 9d204250eecc3354998898a12858979ead41f4af Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 24 May 2026 19:16:02 +0900 Subject: [PATCH 2/5] docs(keyword-detector): rewrite AGENTS.md for prompts-core migration Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/hooks/keyword-detector/AGENTS.md | 81 ++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/src/hooks/keyword-detector/AGENTS.md b/src/hooks/keyword-detector/AGENTS.md index ed2fee805..6ddd69c98 100644 --- a/src/hooks/keyword-detector/AGENTS.md +++ b/src/hooks/keyword-detector/AGENTS.md @@ -1,55 +1,91 @@ -# src/hooks/keyword-detector/ — Mode Keyword Injection +# src/hooks/keyword-detector/ -- Mode Keyword Injection -**Generated:** 2026-05-15 +**Generated:** 2026-05-24 ## OVERVIEW -Transform Tier hook on `messages.transform`. Scans first user message for mode keywords (ultrawork, search, analyze, team) and injects mode-specific system prompts. +Transform Tier hook on `messages.transform`. Scans the first user message for mode keywords and injects mode-specific system prompts. The detector and routing logic stay in `src/hooks/keyword-detector/`; prompt bodies now live in [`packages/prompts-core/prompts/`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/) so they can be shared by future harness adapters. + +This matches the package layering direction in [`ROADMAP.md`](file:///Users/yeongyu/local-workspaces/omo/ROADMAP.md): `packages/prompts-core` owns static prompt content, while this OpenCode hook owns keyword detection, model routing, and message injection. ## KEYWORDS | Keyword | Pattern | Effect | |---------|---------|--------| -| `ultrawork` / `ulw` | `/\b(ultrawork|ulw)\b/i` | Full orchestration mode — parallel agents, deep exploration, relentless execution | +| `ultrawork` / `ulw` | `/\b(ultrawork|ulw)\b/i` | Full orchestration mode: parallel agents, deep exploration, relentless execution | | Search mode | `SEARCH_PATTERN` (from `search/`) | Web/doc search focus prompt injection | | Analyze mode | `ANALYZE_PATTERN` (from `analyze/`) | Deep analysis mode prompt injection | | Team mode | `TEAM_PATTERN` (from `team/`) | Forces orchestration via `team_*` tools when user invokes `team mode` / `team-mode` / `team_mode` / `teammode`; instructs user to enable `team_mode.enabled` if tools are absent and reminds lead to run the closure sequence once every task is terminal | +| Hyperplan mode | `HYPERPLAN_PATTERN` (from `hyperplan/`) | Loads the `hyperplan` skill and injects adversarial planning mode guidance | +| Hyperplan-ultrawork combo | `HYPERPLAN_ULTRAWORK_PATTERN` (from `constants.ts`) | Prepends the combo banner, requires the `hyperplan` skill, then appends the routed ultrawork message | ## STRUCTURE ``` keyword-detector/ ├── index.ts # Barrel export -├── hook.ts # createKeywordDetectorHook() — chat.message handler +├── hook.ts # createKeywordDetectorHook() chat.message handler ├── detector.ts # detectKeywordsWithType() + extractPromptText() ├── constants.ts # KEYWORD_DETECTORS array, re-exports from submodules ├── types.ts # KeywordDetector, DetectedKeyword types ├── ultrawork/ -│ ├── index.ts -│ ├── message.ts # getUltraworkMessage() — dynamic prompt by agent/model -│ └── isPlannerAgent.ts +│ ├── index.ts # getUltraworkMessage() router +│ ├── source-detector.ts # agent/model routing helpers +│ ├── default.ts # thin loader for prompts-core/prompts/ultrawork/default.md +│ ├── gpt.ts # thin loader for prompts-core/prompts/ultrawork/gpt.md +│ ├── gemini.ts # thin loader for prompts-core/prompts/ultrawork/gemini.md +│ └── planner.ts # thin loader for prompts-core/prompts/ultrawork/planner.md ├── search/ │ ├── index.ts -│ ├── pattern.ts # SEARCH_PATTERN regex -│ └── message.ts # SEARCH_MESSAGE +│ └── default.ts # SEARCH_PATTERN + SEARCH_MESSAGE from prompts-core mode prompt ├── analyze/ │ ├── index.ts -│ └── default.ts # ANALYZE_PATTERN + ANALYZE_MESSAGE -└── team/ +│ └── default.ts # ANALYZE_PATTERN + ANALYZE_MESSAGE from prompts-core mode prompt +├── team/ +│ ├── index.ts +│ └── default.ts # TEAM_PATTERN + TEAM_MESSAGE from prompts-core mode prompt +└── hyperplan/ ├── index.ts - └── default.ts # TEAM_PATTERN + TEAM_MESSAGE + └── default.ts # HYPERPLAN_PATTERN + HYPERPLAN_MESSAGE from prompts-core mode prompt ``` +## PROMPT CONTENT LOCATIONS + +| Prompt family | Markdown source | +|---------------|-----------------| +| Ultrawork default | [`packages/prompts-core/prompts/ultrawork/default.md`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/ultrawork/default.md) | +| Ultrawork GPT | [`packages/prompts-core/prompts/ultrawork/gpt.md`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/ultrawork/gpt.md) | +| Ultrawork Gemini | [`packages/prompts-core/prompts/ultrawork/gemini.md`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/ultrawork/gemini.md) | +| Ultrawork planner | [`packages/prompts-core/prompts/ultrawork/planner.md`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/ultrawork/planner.md) | +| Search mode | [`packages/prompts-core/prompts/mode/search.md`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/mode/search.md) | +| Analyze mode | [`packages/prompts-core/prompts/mode/analyze.md`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/mode/analyze.md) | +| Team mode | [`packages/prompts-core/prompts/mode/team.md`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/mode/team.md) | +| Hyperplan mode | [`packages/prompts-core/prompts/mode/hyperplan.md`](file:///Users/yeongyu/local-workspaces/omo/packages/prompts-core/prompts/mode/hyperplan.md) | + +The `src/hooks/keyword-detector/{search,analyze,team,hyperplan}/default.ts` files keep the regex triggers in the hook layer and import the markdown-backed constants from `@oh-my-opencode/prompts-core`. The ultrawork files import markdown with Bun's `.md` text loader so the exact prompt bytes are bundled into `dist/index.js`. + +## ULTRAWORK VARIANT ROUTING + +[`ultrawork/source-detector.ts`](file:///Users/yeongyu/local-workspaces/omo/src/hooks/keyword-detector/ultrawork/source-detector.ts) decides the ultrawork source in priority order: + +1. Planner agents (`prometheus`, `planner`, or normalized `plan`) route to `planner.md`. +2. GPT family models, as detected by `isGptModel(modelID)`, route to `gpt.md`. +3. Gemini family models, as detected by `isGeminiModel(modelID)`, route to `gemini.md`. +4. Everything else routes to `default.md`. + +[`ultrawork/index.ts`](file:///Users/yeongyu/local-workspaces/omo/src/hooks/keyword-detector/ultrawork/index.ts) exposes `getUltraworkMessage(agentName, modelID)`, switches on that source, and returns the loaded markdown body. + ## DETECTION LOGIC ``` chat.message (user input) - → extractPromptText(parts) - → isSystemDirective? → skip - → removeSystemReminders(text) # strip blocks - → detectKeywordsWithType(cleanText, agentName, modelID, disabledKeywords) - → isPlannerAgent(agentName)? → filter out ultrawork - → for each detected keyword: inject mode message into output + -> extractPromptText(parts) + -> isSystemDirective? skip + -> removeSystemReminders(text) # strip blocks + -> detectKeywordsWithType(cleanText, agentName, modelID, disabledKeywords) + -> isNonOmoAgent(agentName)? filter keyword injection + -> isPlannerAgent(agentName)? filter standalone ultrawork + -> for each detected keyword: inject mode message into output ``` ## CONFIG @@ -57,17 +93,20 @@ chat.message (user input) ```jsonc { "keyword_detector": { - // Skip injection for any keyword in this list. Allowed: "ultrawork", "search", "analyze", "team". + // Skip injection for any keyword in this list. + // Allowed: "ultrawork", "search", "analyze", "team", "hyperplan", "hyperplan-ultrawork". "disabled_keywords": ["search", "analyze"] } } ``` -Default: empty/missing → all four detectors active. Schema lives at [src/config/schema/keyword-detector.ts](../../config/schema/keyword-detector.ts). +Default: empty/missing means every detector is active. Schema lives at [`src/config/schema/keyword-detector.ts`](file:///Users/yeongyu/local-workspaces/omo/src/config/schema/keyword-detector.ts). ## GUARDS - **System directive skip**: Messages tagged as system directives are not scanned (prevents infinite loops) - **Planner agent filter**: Prometheus/plan agents do not receive `ultrawork` injection +- **Non-OMO agent filter**: OpenCode built-in Builder/Plan agents do not receive keyword injection - **Session agent tracking**: Uses `getSessionAgent()` to get actual agent (not just input hint) - **Model-aware messages**: `getUltraworkMessage(agentName, modelID)` adapts message to active model +- **Prompt byte baselines**: `mode-prompt-baseline.test.ts` pins mode prompt hashes; `ultrawork/ultrawork-byte-exactness.test.ts` pins ultrawork prompt hashes From e0b37b46d5c631e1fdd81b58acf2d9878fb4792a Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 24 May 2026 19:16:03 +0900 Subject: [PATCH 3/5] test(prometheus): add byte-exact characterization test Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../prometheus-byte-exactness.test.ts | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/agents/prometheus/prometheus-byte-exactness.test.ts diff --git a/src/agents/prometheus/prometheus-byte-exactness.test.ts b/src/agents/prometheus/prometheus-byte-exactness.test.ts new file mode 100644 index 000000000..882c33e63 --- /dev/null +++ b/src/agents/prometheus/prometheus-byte-exactness.test.ts @@ -0,0 +1,81 @@ +/// + +import { describe, expect, test } from "bun:test" +import { createHash } from "node:crypto" +import { getPrometheusPrompt } from "./system-prompt" + +type PrometheusPromptBaseline = { + readonly name: string + readonly model: string | undefined + readonly disabledTools: readonly string[] + readonly sha256: string + readonly shouldContainQuestionTool: boolean +} + +const PROMETHEUS_PROMPT_BASELINES: readonly PrometheusPromptBaseline[] = [ + { + name: "default-enabled", + model: undefined, + disabledTools: [], + sha256: "7cd6dcc764c4b6c7cca61cf3878a1a2b2fb91836b38cbd0ed348d3e778cea4d9", + shouldContainQuestionTool: true, + }, + { + name: "default-question-disabled", + model: undefined, + disabledTools: ["question"], + sha256: "db181638b60c222e5238daa8c090b8b908235d4a1cef7ff760839d4200195f59", + shouldContainQuestionTool: false, + }, + { + name: "gpt-enabled", + model: "gpt-5.5", + disabledTools: [], + sha256: "95e42fb8112a6aac3d702fa40ec4a8f89923acd239e1041646ed5a0fd2a9feb9", + shouldContainQuestionTool: true, + }, + { + name: "gpt-question-disabled", + model: "gpt-5.5", + disabledTools: ["question"], + sha256: "8792637920d271caec5675e63ed6685b1e5e6e292824fd6cf3f9a699e83a42fe", + shouldContainQuestionTool: false, + }, + { + name: "gemini-enabled", + model: "gemini-3.1-pro", + disabledTools: [], + sha256: "df846993f69aef852bfe14569231453c673d369d69229f6e67f5af0915c05a1d", + shouldContainQuestionTool: true, + }, + { + name: "gemini-question-disabled", + model: "gemini-3.1-pro", + disabledTools: ["question"], + sha256: "f9f9b7498c681a7d98a388a2e2aaf875227a145b4f824277ef54ed3a8d80106b", + shouldContainQuestionTool: false, + }, +] + +describe("Prometheus prompt byte exactness", () => { + test("#given captured Prometheus prompt baselines #then every variant keeps the same bytes", () => { + for (const baseline of PROMETHEUS_PROMPT_BASELINES) { + const prompt = getPrometheusPrompt(baseline.model, baseline.disabledTools) + + expect(prompt.length, baseline.name).toBeGreaterThan(0) + expect(hashPrompt(prompt), baseline.name).toBe(baseline.sha256) + } + }) + + test("#given Question tool availability changes #then Question examples follow disabledTools", () => { + for (const baseline of PROMETHEUS_PROMPT_BASELINES) { + const prompt = getPrometheusPrompt(baseline.model, baseline.disabledTools) + + expect(prompt.includes("Question({"), baseline.name).toBe(baseline.shouldContainQuestionTool) + } + }) +}) + +function hashPrompt(prompt: string): string { + return createHash("sha256").update(prompt).digest("hex") +} From d18cadb1ab134d1211d86a5c1314f0fe5c2ed9d0 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 24 May 2026 19:16:03 +0900 Subject: [PATCH 4/5] test(ultrawork): add byte-exact characterization test Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../ultrawork-byte-exactness.test.ts | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/hooks/keyword-detector/ultrawork/ultrawork-byte-exactness.test.ts diff --git a/src/hooks/keyword-detector/ultrawork/ultrawork-byte-exactness.test.ts b/src/hooks/keyword-detector/ultrawork/ultrawork-byte-exactness.test.ts new file mode 100644 index 000000000..f5fe2dd92 --- /dev/null +++ b/src/hooks/keyword-detector/ultrawork/ultrawork-byte-exactness.test.ts @@ -0,0 +1,62 @@ +/// + +import { describe, expect, test } from "bun:test" +import { createHash } from "node:crypto" +import { getUltraworkMessage, getUltraworkSource } from "./index" +import type { UltraworkSource } from "./source-detector" + +type UltraworkPromptBaseline = { + readonly name: string + readonly agentName: string + readonly modelID: string + readonly expectedSource: UltraworkSource + readonly sha256: string +} + +const ULTRAWORK_PROMPT_BASELINES: readonly UltraworkPromptBaseline[] = [ + { + name: "default", + agentName: "sisyphus", + modelID: "claude-sonnet-4-6", + expectedSource: "default", + sha256: "78aa43e2e2b7db307827d9ddda30a4c6a24aa35a9255efe1ee39a4476d71acca", + }, + { + name: "gpt", + agentName: "sisyphus", + modelID: "gpt-5.5", + expectedSource: "gpt", + sha256: "8f31f0053256914e94605944b28e123c584a0ad093e0d44d5ad66da009a632ae", + }, + { + name: "gemini", + agentName: "sisyphus", + modelID: "gemini-3.1-pro", + expectedSource: "gemini", + sha256: "5c5766549e868e7a1c87252e742e491b7015138948c6e26fb704346bb55d5d7c", + }, + { + name: "planner", + agentName: "prometheus", + modelID: "gpt-5.5", + expectedSource: "planner", + sha256: "8897b3a11b61c12a02bfba13a76c80742bc4e5356cfc30e2f0c38464aa587bf3", + }, +] + +describe("Ultrawork prompt byte exactness", () => { + test("#given captured ultrawork prompt baselines #then every routed source keeps the same bytes", () => { + for (const baseline of ULTRAWORK_PROMPT_BASELINES) { + const source = getUltraworkSource(baseline.agentName, baseline.modelID) + const prompt = getUltraworkMessage(baseline.agentName, baseline.modelID) + + expect(source, baseline.name).toBe(baseline.expectedSource) + expect(prompt.length, baseline.name).toBeGreaterThan(0) + expect(hashPrompt(prompt), baseline.name).toBe(baseline.sha256) + } + }) +}) + +function hashPrompt(prompt: string): string { + return createHash("sha256").update(prompt).digest("hex") +} From 8f09c02cc00394b025b4ed2b99aab2a3329a47b3 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 24 May 2026 19:16:03 +0900 Subject: [PATCH 5/5] docs: scrub remaining references to migrated files Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/agents/AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/agents/AGENTS.md b/src/agents/AGENTS.md index 3943b9fe9..f0a215bd8 100644 --- a/src/agents/AGENTS.md +++ b/src/agents/AGENTS.md @@ -73,7 +73,7 @@ agents/ ├── metis.ts # Pre-planning ├── momus.ts # Plan review ├── atlas/agent.ts # Todo orchestrator -├── prometheus/ # Strategic planner — system-prompt.ts, identity-constraints.ts, interview-mode.ts, plan-template.ts, gemini.ts, gpt.ts +├── prometheus/ # Strategic planner thin loaders: system-prompt.ts, gemini.ts, gpt.ts; prompt content in packages/prompts-core/prompts/prometheus/ ├── types.ts # BuiltinAgentName, AgentMode, AgentConfig ├── builtin-agents.ts # agentSources registry (10 → 11 with sisyphus-junior) ├── builtin-agents/ # maybeCreateXXXConfig conditional factories + general-agents.ts + available-skills.ts