* feat(auth): add multi-account types and storage layer
Add foundation for multi-account Google Antigravity auth:
- ModelFamily, AccountTier, RateLimitState types for rate limit tracking
- AccountMetadata, AccountStorage, ManagedAccount interfaces
- Cross-platform storage module with XDG_DATA_HOME/APPDATA support
- Comprehensive test coverage for storage operations
🤖 Generated with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
* feat(auth): implement AccountManager for multi-account rotation
Add AccountManager class with automatic account rotation:
- Per-family rate limit tracking (claude, gemini-flash, gemini-pro)
- Paid tier prioritization in rotation logic
- Round-robin account selection within tier pools
- Account add/remove operations with index management
- Storage persistence integration
🤖 Generated with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
* feat(auth): add CLI prompts for multi-account setup
Add @clack/prompts-based CLI utilities:
- promptAddAnotherAccount() for multi-account flow
- promptAccountTier() for free/paid tier selection
- Non-TTY environment handling (graceful skip)
🤖 Generated with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
* feat(auth): integrate multi-account OAuth flow into plugin
Enhance OAuth flow for multi-account support:
- Prompt for additional accounts after first OAuth (up to 10)
- Collect email and tier for each account
- Save accounts to storage via AccountManager
- Load AccountManager in loader() from stored accounts
- Toast notifications for account authentication success
- Backward compatible with single-account flow
🤖 Generated with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
* feat(auth): add rate limit rotation to fetch interceptor
Integrate AccountManager into fetch for automatic rotation:
- Model family detection from URL (claude/gemini-flash/gemini-pro)
- Rate limit detection (429 with retry-after > 5s, 5xx errors)
- Mark rate-limited accounts and rotate to next available
- Recursive retry with new account on rotation
- Lazy load accounts from storage on first request
- Debug logging for account switches
🤖 Generated with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
* feat(cli): add auth account management commands
Add CLI commands for managing Google Antigravity accounts:
- `auth list`: Show all accounts with email, tier, rate limit status
- `auth remove <index|email>`: Remove account by index or email
- Help text with usage examples
- Active account indicator and remaining rate limit display
🤖 Generated with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
* refactor(auth): address review feedback - remove duplicate ManagedAccount and reuse fetch function
- Remove unused ManagedAccount interface from types.ts (duplicate of accounts.ts)
- Reuse fetchFn in rate limit retry instead of creating new fetch closure
Preserves cachedTokens, cachedProjectId, fetchInstanceId, accountsLoaded state
* fix(auth): address Cubic review feedback (8 issues)
P1 fixes:
- storage.ts: Use mode 0o600 for OAuth credentials file (security)
- fetch.ts: Return original 5xx status instead of synthesized 429
- accounts.ts: Adjust activeIndex/currentIndex in removeAccount
- plugin.ts: Fix multi-account migration to split on ||| not |
P2 fixes:
- cli.ts: Remove confusing cancel message when returning default
- auth.ts: Use strict parseInt check to prevent partial matches
- storage.test.ts: Use try/finally for env var cleanup
* refactor(test): import ManagedAccount from accounts.ts instead of duplicating
* fix(auth): address Oracle review findings (P1/P2)
P1 fixes:
- Clear cachedProjectId on account change to prevent stale project IDs
- Continue endpoint fallback for single-account users on rate limit
- Restore access/expires tokens from storage for non-active accounts
- Re-throw non-ENOENT filesystem errors (keep returning null for parse errors)
- Use atomic write (temp file + rename) for account storage
P2 fixes:
- Derive RateLimitState type from ModelFamily using mapped type
- Add MODEL_FAMILIES constant and use dynamic iteration in clearExpiredRateLimits
- Add missing else branch in storage.test.ts env cleanup
- Handle open() errors gracefully with user-friendly toast message
Tests updated to reflect correct behavior for token restoration.
* fix(auth): address Cubic review round 2 (5 issues)
P1: Return original 429/5xx response on last endpoint instead of generic 503
P2: Use unique temp filename (pid+timestamp) and cleanup on rename failure
P2: Clear cachedProjectId when first account introduced (lastAccountIndex null)
P3: Add console.error logging to open() catch block
* test(auth): add AccountManager removeAccount index tests
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
* test(auth): add storage layer security and atomicity tests
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
* fix(auth): address Cubic review round 3 (4 issues)
P1 Fixes:
- plugin.ts: Validate refresh_token before constructing first account
- plugin.ts: Validate additionalTokens.refresh_token before pushing accounts
- fetch.ts: Reset cachedTokens when switching accounts during rotation
P2 Fixes:
- fetch.ts: Improve model-family detection (parse model from body, fallback to URL)
* fix(auth): address Cubic review round 4 (3 issues)
P1 Fixes:
- plugin.ts: Close serverHandle before early return on missing refresh_token
- plugin.ts: Close additionalServerHandle before continue on missing refresh_token
P2 Fixes:
- fetch.ts: Remove overly broad 'pro' matching in getModelFamilyFromModelName
* fix(auth): address Cubic review round 5 (9 issues)
P1 Fixes:
- plugin.ts: Close additionalServerHandle after successful account auth
- fetch.ts: Cancel response body on 429/5xx to prevent connection leaks
P2 Fixes:
- plugin.ts: Close additionalServerHandle on OAuth error/missing code
- plugin.ts: Close additionalServerHandle on verifier mismatch
- auth.ts: Set activeIndex to -1 when all accounts removed
- storage.ts: Use shared getDataDir utility for consistent paths
- fetch.ts: Catch loadAccounts IO errors with graceful fallback
- storage.test.ts: Improve test assertions with proper error tracking
* feat(antigravity): add system prompt and thinking config constants
* feat(antigravity): add reasoning_effort and Gemini 3 thinkingLevel support
* feat(antigravity): inject system prompt into all requests
* feat(antigravity): integrate thinking config and system prompt in fetch layer
* feat(auth): auto-open browser for OAuth login on all platforms
* fix(auth): add alias2ModelName for Antigravity Claude models
Root cause: Antigravity API expects 'claude-sonnet-4-5-thinking' but we were
sending 'gemini-claude-sonnet-4-5-thinking'. Ported alias mapping from
CLIProxyAPI antigravity_executor.go:1328-1347.
Transforms:
- gemini-claude-sonnet-4-5-thinking → claude-sonnet-4-5-thinking
- gemini-claude-opus-4-5-thinking → claude-opus-4-5-thinking
- gemini-3-pro-preview → gemini-3-pro-high
- gemini-3-flash-preview → gemini-3-flash
* fix(auth): add requestType and toolConfig for Antigravity API
Missing required fields from CLIProxyAPI implementation:
- requestType: 'agent'
- request.toolConfig.functionCallingConfig.mode: 'VALIDATED'
- Delete request.safetySettings
Also strip 'antigravity-' prefix before alias transformation.
* fix(auth): remove broken alias2ModelName transformations for Gemini 3
CLIProxyAPI's alias mappings don't work with public Antigravity API:
- gemini-3-pro-preview → gemini-3-pro-high (404!)
- gemini-3-flash-preview → gemini-3-flash (404!)
Tested: -preview suffix names work, transformed names return 404.
Keep only gemini-claude-* prefix stripping for future Claude support.
* fix(auth): implement correct alias2ModelName transformations for Antigravity API
Implements explicit switch-based model name mappings for Antigravity API.
Updates SANDBOX endpoint constants to clarify quota/availability behavior.
Fixes test expectations to match new transformation logic.
🤖 Generated with assistance of OhMyOpenCode
---------
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
* docs: update opencode-antigravity-auth version to 1.2.8
* docs: update opencode-antigravity-auth to 1.2.8 in localized READMEs
---------
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
* fix: respect disabled_hooks for keyword-detector in claude-code-hooks
The keyword detection in claude-code-hooks was running regardless of
whether keyword-detector was in disabled_hooks. This caused analyze
and search modes to trigger even when explicitly disabled.
Pass keywordDetectorDisabled flag to createClaudeCodeHooksHook and
skip keyword detection when the hook is disabled.
Fixes#530
* refactor: restore keyword types in log output
Add types array back to keyword detection log for better observability
* revert: undo PR #543 changes (bun shell GC crash was misdiagnosed)
This reverts commit 4a38e70 (PR #543) and 2064568 (follow-up fix).
## Why This Revert
The original diagnosis was incorrect. PR #543 assumed Bun's
ShellInterpreter GC bug was causing Windows crashes, but further
investigation revealed the actual root cause:
**The crash occurs when oh-my-opencode's session-notification runs
alongside external notification plugins (e.g., @mohak34/opencode-notifier).**
Evidence:
- User removed opencode-notifier plugin → crashes stopped
- Release version (with original ctx.$ code) works fine when used alone
- No widespread crash reports from users without external notifiers
- Both plugins listen to session.idle and send concurrent notifications
The real issue is a conflict between two notification systems:
1. oh-my-opencode: ctx.$ → PowerShell → Windows.UI.Notifications
2. opencode-notifier: node-notifier → SnoreToast.exe
A proper fix will detect and handle this conflict gracefully.
Refs: #543, oven-sh/bun#23177, oven-sh/bun#24368
See: docs/CRASH_INVESTIGATION_TIMELINE.md (in follow-up commit)
* fix(session-notification): detect and avoid conflict with external notification plugins
When oh-my-opencode's session-notification runs alongside external
notification plugins like opencode-notifier, both listen to session.idle
and send concurrent notifications. This can cause crashes on Windows
due to resource contention between different notification mechanisms:
- oh-my-opencode: ctx.$ → PowerShell → Windows.UI.Notifications
- opencode-notifier: node-notifier → SnoreToast.exe
This commit adds:
1. External plugin detection (checks opencode.json for known notifiers)
2. Auto-disable of session-notification when conflict detected
3. Console warning explaining the situation
4. Config option 'notification.force_enable' to override
Known notification plugins detected:
- opencode-notifier
- @mohak34/opencode-notifier
- mohak34/opencode-notifier
This is the actual fix for the Windows crash issue previously
misdiagnosed as a Bun.spawn GC bug (PR #543).
Refs: #543
* docs: add crash investigation timeline explaining the real root cause
Documents the investigation journey from initial misdiagnosis (Bun GC bug)
to discovering the actual root cause (notification plugin conflict).
Key findings:
- PR #543 was based on incorrect assumption
- The real issue is concurrent notification plugins
- oh-my-opencode + opencode-notifier = crash on Windows
- Either plugin alone works fine
* fix: address review feedback - add PowerShell escaping and use existing JSONC parser
- Add back single-quote escaping for PowerShell soundPath to prevent command failures
- Replace custom stripJsonComments with existing parseJsoncSafe from jsonc-parser
- All 655 tests pass
---------
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
- Add Arthur Guiot quote to zh-cn and ja
- Add 苔硯:こけすずり quote with localized translations
- Remove quotes not present in English (z80.eth, RyanOnThePath, Sigrid)
The remote MCP endpoint https://mcp.exa.ai/mcp requires API key authentication
via x-api-key header. Without this, the connection times out waiting for auth.
This change:
- Reads EXA_API_KEY from environment variable
- Passes it as x-api-key header when available
- Updates RemoteMcpConfig type to support optional headers
Co-authored-by: Junho Yeo <i@junho.io>
Add documentation for the 'plugins' and 'plugins_override' options
in the claude_code configuration section. This allows users to:
- Disable all Claude Code marketplace plugins with 'plugins: false'
- Selectively disable specific plugins using 'plugins_override'
These options were missing from the documentation but are supported
by the codebase.
- Add assessment phase before searching to reduce unnecessary tool calls
- Change mandatory minimum parallel calls to suggested ranges
- Allow direct answers from training knowledge for well-known APIs
Previously, concurrency was released in finally block AFTER prompt completion.
This caused queued tasks to remain blocked while prompt hangs.
Now release happens BEFORE prompt, allowing next queued task to start immediately
when current task completes, regardless of prompt success/failure.
Also added early release on session creation error for proper cleanup.
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
The `forceZsh` option on Linux/macOS would use a hardcoded zshPath
without checking if zsh actually exists on the system. This caused
hook commands to fail silently with exit code 127 on systems without
zsh installed.
Changes:
- Always verify zsh exists via findZshPath() before using it
- Fall back to bash -lc if zsh not found (preserves login shell PATH)
- Fall through to spawn with shell:true if neither found
The bash fallback ensures user PATH from .profile/.bashrc is available,
which is important for hooks that depend on custom tool locations.
Tested with opencode v1.1.3 - PreToolUse hooks now execute correctly
on systems without zsh.
Co-authored-by: Anas Viber <ananas-viber@users.noreply.github.com>
Replace Bun shell template literals (ctx.$) with node:child_process.spawn
to work around Bun's ShellInterpreter garbage collection bug on Windows.
This bug causes segmentation faults in deinitFromFinalizer during heap
sweeping when shell operations are used repeatedly over time.
Bug references:
- oven-sh/bun#23177 (closed incomplete)
- oven-sh/bun#24368 (still open)
- Pending fix: oven-sh/bun#24093
The fix applies to all platforms for consistency and safety.
* refactor(agents): remove unused model references
Consistent cleanup of agent model references across all agent files.
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
* fix(agents): use glm-4.7-free as default librarian model
🤖 Generated with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
* make playwright skill to be called more
Claude Code uses whitelist-based tools format which is semantically
different from OpenCode's denylist-based permission system. Apply
migration only to plugin agents for compatibility.
🤖 Generated with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) assistance
Ports the refactor command from ~/.config/opencode/command/refactor.md to the project as a builtin command.
The /refactor command provides deterministic, LSP/AST-aware refactoring with:
- Automatic intent analysis and codebase mapping
- Risk assessment with test coverage verification
- Detailed planning via Plan agent
- Step-by-step execution with continuous verification
- Zero-regression guarantees via comprehensive testing
Supports multiple refactoring scopes (file/module/project) and strategies (safe/aggressive).
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)