Remove duplicate removeCodeBlocks() call in keyword-detector/index.ts.
The detectKeywordsWithType() function already calls removeCodeBlocks() internally, so calling it before passing the text was redundant and caused unnecessary double processing.
* refactor(keyword-detector): split constants into domain-specific modules
* feat(shared): add requiresAnyModel and isAnyFallbackModelAvailable
* feat(config): add hephaestus to agent schemas
* feat(agents): add Hephaestus autonomous deep worker
* feat(cli): update model-fallback for hephaestus support
* feat(plugin): add hephaestus to config handler with ordering
* test(delegate-task): update tests for hephaestus agent
* docs: update AGENTS.md files for hephaestus
* docs: add hephaestus to READMEs
* chore: regenerate config schema
* fix(delegate-task): bypass requiresModel check when user provides explicit config
* docs(hephaestus): add 4-part context structure for explore/librarian prompts
* docs: fix review comments from cubic (non-breaking changes)
- Move Hephaestus from Primary Agents to Subagents (uses own fallback chain)
- Fix Hephaestus fallback chain documentation (claude-opus-4-5 → gemini-3-pro)
- Add settings.local.json to claude-code-hooks config sources
- Fix delegate_task parameters in ultrawork prompt (agent→subagent_type, background→run_in_background, add load_skills)
- Update line counts in AGENTS.md (index.ts: 788, manager.ts: 1440)
* docs: fix additional documentation inconsistencies from oracle review
- Fix delegate_task parameters in Background Agents example (docs/features.md)
- Fix Hephaestus fallback chain in root AGENTS.md to match model-requirements.ts
* docs: clarify Hephaestus has no fallback (requires gpt-5.2-codex only)
Hephaestus uses requiresModel constraint - it only activates when gpt-5.2-codex
is available. The fallback chain in code is unreachable, so documentation
should not mention fallbacks.
* fix(hephaestus): remove unreachable fallback chain entries
Hephaestus has requiresModel: gpt-5.2-codex which means the agent only
activates when that specific model is available. The fallback entries
(claude-opus-4-5, gemini-3-pro) were unreachable and misleading.
---------
Co-authored-by: justsisyphus <justsisyphus@users.noreply.github.com>
Automated system messages with <system-reminder> tags were incorrectly
triggering [search-mode], [analyze-mode], and other keyword modes when
they contained words like "search", "find", "explore", etc.
Changes:
- Add removeSystemReminders() to strip <system-reminder> content before keyword detection
- Add hasSystemReminder() utility function
- Update keyword-detector to clean text before pattern matching
- Add comprehensive test coverage for system-reminder filtering
Fixes issue where automated system notifications caused agents to
incorrectly enter MAXIMUM SEARCH EFFORT mode.
Co-authored-by: TheEpTic <git@eptic.me>
- Replace collector.register() with direct output.parts[textIndex].text modification
- All keyword types (ultrawork, search, analyze) now prepend to user message text
- Message format: keyword message + '---' separator + original text
- Update tests to verify text transformation instead of collector registration
- All 18 tests pass
Skip all keyword detection for background task sessions to prevent mode
injection (e.g., [analyze-mode], [search-mode]) which incorrectly triggers
Prometheus planner restrictions on Sisyphus sessions.
This aligns with the existing pattern used in:
- sisyphus-orchestrator (line 504)
- todo-continuation-enforcer (line 303)
- session-notification (line 278)
Closes#713
Bug: When switching from Prometheus to Sisyphus, the Prometheus ultrawork
template was still injected because:
1. setSessionAgent() only sets on first call, ignoring subsequent updates
2. keyword-detector relied solely on input.agent which could be stale
Fix:
- Use updateSessionAgent() instead of setSessionAgent() in index.ts
- keyword-detector now uses getSessionAgent() as primary source, fallback to input.agent
- Added tests for agent switch scenario
- Add shared/system-directive.ts with SYSTEM_DIRECTIVE_PREFIX constant
- Unify all system message prefixes to [SYSTEM DIRECTIVE: OH-MY-OPENCODE - ...]
- Add isSystemDirective() filter to keyword-detector to skip system messages
- Update prometheus-md-only tests to use new prefix constants
Allow optional model variant config for agents and categories.
Propagate category variants into task model payloads so
category-driven runs inherit provider-specific variants.
Closes: #647
- keyword-detector now registers keywords to ContextCollector
- context-injector consumes and injects via chat.message hook
- Removed keyword detection logic from claude-code-hooks
- Hook order: keyword-detector → context-injector → claude-code-hooks
- ultrawork now works even when claude-code-hooks is disabled
The keyword-detector was using ctx.client.session.get() to check parentID for
determining subagent sessions, but this API didn't reliably return parentID.
This caused non-ultrawork keywords (search, analyze) to be injected in subagent
sessions when they should only work in main sessions.
Changed to use getMainSessionID() comparison, consistent with other hooks like
session-notification and todo-continuation-enforcer.
- Replace unreliable parentID API check with mainSessionID comparison
- Add comprehensive test coverage for session filtering behavior
- Remove unnecessary session.get API call
- Fix#542: slashcommand tool returns empty content for skills
- Add lazyContentLoader to CommandInfo type
- Load skill content in formatLoadedCommand when content is empty
- Filter non-ultrawork keywords in subagent sessions
Wire agent information through keyword detector hooks:
- Pass input.agent to detectKeywordsWithType in keyword-detector hook
- Pass input.agent to detectKeywordsWithType in claude-code-hooks
- Enables agent-aware ultrawork message generation
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
Move keyword detection from experimental.chat.messages.transform to claude-code-hooks
chat.message handler. Uses proven injectHookMessage file system approach for reliable
context injection.
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Add ContextCollector class for managing and merging context entries across sessions
- Add types and interfaces for context management (ContextEntry, ContextPriority, PendingContext)
- Create context-injector hook for injection coordination
- Refactor keyword-detector to use context-injector instead of hook-message-injector
- Update src/index.ts to initialize context-injector infrastructure
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
* feat(keyword-detector): show toast notification when ultrawork mode is activated
When users trigger ultrawork mode (via 'ultrawork' or 'ulw' keywords), a toast
notification now appears to confirm the mode is active. The notification is
shown once per session to avoid spamming.
Changes:
- Add detectKeywordsWithType() to identify which keyword type triggered
- Show 'Ultrawork Mode Activated' toast with success variant
- Track notified sessions to prevent duplicate toasts
Closes#392
* fix(keyword-detector): fix index bug in detectKeywordsWithType and add error logging
- Fix P1: detectKeywordsWithType now maps before filtering to preserve
original KEYWORD_DETECTORS indices. Previously, the index used was from
the filtered array, causing incorrect type assignment (e.g., 'search'
match would incorrectly return 'ultrawork' type).
- Fix P2: Replace silent .catch(() => {}) with proper error logging using
the log function for easier debugging when toast notifications fail.
---------
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Previously, first messages were skipped entirely to avoid interfering with title generation.
Now, keywords detected on the first message are injected directly into the message parts
instead of using the hook message injection system, allowing keywords like 'ultrawork' to
activate on the first message of a session.
This change:
- Removes the early return that skipped first message keyword detection
- Moves keyword context generation before the isFirstMessage check
- For first messages: transforms message parts directly by prepending keyword context
- For subsequent messages: maintains existing hook message injection behavior
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
Remove first-message-only restriction and move keyword injection to chat.message
hook for consistent keyword presence across all messages.
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Add content validation in injectHookMessage() to prevent empty hook content injection
- Add logging to claude-code-hooks and keyword-detector for better debugging
- Document timing issues in empty-message-sanitizer comments
- Update README with improved setup instructions
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)