- Add follow?: boolean option to GlobOptions interface
- Change buildRgArgs to use !== false pattern for hidden and follow flags
- Change buildFindArgs to use === false pattern, add -L for symlinks
- Change buildPowerShellCommand to use !== false pattern for hidden
- Remove -FollowSymlink from PowerShell (unsupported in PS 5.1)
- Export build functions for testing
- Add comprehensive BDD-style tests (18 tests, 21 assertions)
Note: Symlink following via -FollowSymlink is not supported in Windows
PowerShell 5.1. OpenCode auto-downloads ripgrep which handles symlinks
via --follow flag. PowerShell fallback is a safety net that rarely triggers.
Fixes#631
* fix(config): allow Sisyphus-Junior agent customization via oh-my-opencode.json
Allow users to configure Sisyphus-Junior agent via agents["Sisyphus-Junior"]
in oh-my-opencode.json, removing hardcoded defaults while preserving safety
constraints.
Closes#623
Changes:
- Add "Sisyphus-Junior" to AgentOverridesSchema and OverridableAgentNameSchema
- Create createSisyphusJuniorAgentWithOverrides() helper with guardrails
- Update config-handler to use override helper instead of hardcoded values
- Fix README category wording (runtime presets, not separate agents)
Honored override fields:
- model, temperature, top_p, tools, permission, description, color, prompt_append
Safety guardrails enforced post-merge:
- mode forced to "subagent" (cannot change)
- prompt is append-only (base discipline text preserved)
- blocked tools (task, sisyphus_task, call_omo_agent) always denied
- disable: true ignores override block, uses defaults
Category interaction:
- sisyphus_task(category=...) runs use the base Sisyphus-Junior agent config
- Category model/temperature overrides take precedence at request time
- To change model for a category, set categories.<cat>.model (not agent override)
- Categories are runtime presets applied to Sisyphus-Junior, not separate agents
Tests: 15 new tests in sisyphus-junior.test.ts, 3 new schema tests
Co-Authored-By: Sisyphus <sisyphus@mengmota.com>
* test(sisyphus-junior): add guard assertion for prompt anchor text
Add validation that baseEndIndex is not -1 before using it for ordering
assertion. Previously, if "Dense > verbose." text changed in the base
prompt, indexOf would return -1 and any positive appendIndex would pass.
Co-Authored-By: Sisyphus <sisyphus@mengmota.com>
---------
Co-authored-by: Sisyphus <sisyphus@mengmota.com>
- Add serializeError utility to handle Error instances, plain objects, and nested message paths
- Fix handleSessionError to use serializeError instead of naive String() conversion
- Fix runner.ts catch block to use serializeError for detailed error messages
- Add session.error case to logEventVerbose for better error visibility
- Add comprehensive tests for serializeError function
Fixes error logging in sisyphus-agent workflow where errors were displayed as '[object Object]'
* fix(config): resolve category to model for Prometheus (Planner) agent
When Prometheus (Planner) was configured with only a category (e.g.,
"ultrabrain") and no explicit model, the category was ignored and the
agent fell back to the hardcoded default "anthropic/claude-opus-4-5".
Add resolveModelFromCategoryWithUserOverride() helper that checks user
categories first, then DEFAULT_CATEGORIES, to resolve category names
to their corresponding models. Apply this resolution when building
the Prometheus agent configuration.
Co-Authored-By: Sisyphus <sisyphus@mengmota.com>
* fix(test): use actual implementation instead of local duplicate
Co-Authored-By: Sisyphus <sisyphus@mengmota.com>
* fix(config): apply all category properties, not just model for Prometheus (Planner)
The resolveModelFromCategoryWithUserOverride() helper only extracted
the model field from CategoryConfig, ignoring critical properties like
temperature, top_p, tools, maxTokens, thinking, reasoningEffort, and
textVerbosity. This caused categories like "ultrabrain" (temperature:
0.1) to run with incorrect default temperatures.
Refactor resolveModelFromCategoryWithUserOverride() to
resolveCategoryConfig() that returns the full CategoryConfig. Update
Prometheus (Planner) configuration to apply all category properties
(temperature, top_p, tools, etc.) when a category is specified, matching
the pattern established in Sisyphus-Junior. Explicit overrides still
take precedence during merge.
Co-Authored-By: Sisyphus <sisyphus@mengmota.com>
---------
Co-authored-by: Sisyphus <sisyphus@mengmota.com>
Update CLI commands to use channel-aware version fetching:
- doctor check now reports channel in error messages
- get-local-version uses channel from pinned version
Depends on channel detection from previous commit.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add support for npm dist-tag channels (@beta, @next, @canary) in auto-update mechanism. Users pinned to oh-my-opencode@beta now correctly fetch and compare against beta channel instead of stable latest.
- Add extractChannel() to detect channel from version string
- Modify getLatestVersion() to accept channel parameter
- Update auto-update flow to use channel-aware fetching
- Add comprehensive tests for channel detection and fetching
- Resolves#687
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Use regex /\.sisyphus[/\\]/i instead of checking first path segment. This fixes Windows paths where ctx.directory is parent of the actual project (e.g., project\.sisyphus\drafts\...).
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
- Revert messages.transform-only approach (experimental hook unreliable)
- Inject context in chat.message after claudeCodeHooks runs
- Order: keywordDetector → claudeCodeHooks → contextInjector
- Works independently of claude-code-hooks being enabled/disabled
- Ultrawork content now reliably injected to model
- Remove contextInjector call from chat.message hook chain
- Context injection now only happens in messages.transform hook
- This ensures UI displays original user message while model receives prepended context
- Fixes bug where commit message promised clone behavior but implementation mutated directly
Replace brittle string checks with robust path.resolve/relative validation:
- Fix Windows backslash paths (.sisyphus\plans\x.md) being incorrectly blocked
- Fix case-sensitive extension check (.MD now accepted)
- Add workspace confinement (block paths outside root even if containing .sisyphus)
- Block nested .sisyphus directories (only first segment allowed)
- Block path traversal attempts (.sisyphus/../secrets.md)
- Use ALLOWED_EXTENSIONS and ALLOWED_PATH_PREFIX constants (case-insensitive)
The new isAllowedFile() uses Node's path module for cross-platform compatibility
instead of string includes/endsWith which failed on Windows separators.
- 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
- Add env var check to getCliConfigDir() for config directory override
- Update detectExistingConfigDir() to include env var path in locations
- Add comprehensive tests (7 test cases)
- Document in README
Closes#627
* fix(lsp): cleanup orphaned LSP servers on session.deleted
When parallel background agent tasks complete, their LSP servers (for
repos cloned to /tmp/) remain running until a 5-minute idle timeout.
This causes memory accumulation with heavy parallel Sisyphus usage,
potentially leading to OOM crashes.
This change adds cleanupTempDirectoryClients() to LSPServerManager
(matching the pattern used by SkillMcpManager.disconnectSession())
and calls it on session.deleted events.
The cleanup targets idle LSP clients (refCount=0) for temporary
directories (/tmp/, /var/folders/) where agent tasks clone repos.
* chore: retrigger CI checks