Compare commits

...

110 Commits

Author SHA1 Message Date
YeonGyu-Kim
21ddd55162 fix: prevent agents from duplicating delegated subagent work
Addresses user reports where Sisyphus/Hephaestus would delegate tasks
to explore/librarian subagents but then immediately perform the same
search/work themselves, wasting context and defeating the purpose of
delegation.

Changes:
- Add Anti-Duplication section to dynamic-agent-prompt-builder with
  clear rules: once you delegate, do NOT manually re-do the same search
- Update all agent prompts (sisyphus, hephaestus, sis-junior gemini/gpt)
  to use 'non-overlapping work' instead of 'keep working' after delegation
- Add buildAntiDuplicationSection() with explicit examples of forbidden
  vs allowed behavior after delegation
- Add 'Delegation Trust Rule' to explore section
- Add 'Delegation Duplication' to anti-patterns list

Atlas fixes:
- Add AUTO-CONTINUE POLICY to all Atlas variants (default, gemini, gpt)
  preventing the 'should I continue?' confirmation loop between plan steps
- Fix plan file path in default atlas (.sisyphus/tasks/ -> .sisyphus/plans/)
- Update GPT atlas uncertainty section to only ask questions during initial
  plan analysis, not during execution

Fixes: subagent delegation duplication, Atlas continuation prompting
2026-03-09 12:26:15 +09:00
github-actions[bot]
5137df72d8 @mrosnerr has signed the CLA in code-yeongyu/oh-my-opencode#2328 2026-03-05 18:11:22 +00:00
github-actions[bot]
dd70ce37f0 @hkc5 has signed the CLA in code-yeongyu/oh-my-opencode#2327 2026-03-05 17:56:52 +00:00
github-actions[bot]
7e0a1a133c @mInrOz has signed the CLA in code-yeongyu/oh-my-opencode#2321 2026-03-05 12:42:40 +00:00
YeonGyu-Kim
be606cdfbe Merge pull request #2315 from ualtinok/fix/bgoutputdesc
fix(background-task): clarify timeout unit is milliseconds in description
2026-03-05 20:58:29 +09:00
github-actions[bot]
6a29a373f4 @Wangmerlyn has signed the CLA in code-yeongyu/oh-my-opencode#2318 2026-03-05 11:08:20 +00:00
ismeth
389625cb20 Update constants.ts 2026-03-05 11:41:39 +01:00
ismeth
e916d564a9 fix(background-task): clarify timeout unit is milliseconds in description 2026-03-05 09:05:29 +01:00
github-actions[bot]
3d8f390b9e @Vacbo has signed the CLA in code-yeongyu/oh-my-opencode#2310 2026-03-05 04:20:01 +00:00
YeonGyu-Kim
a61f8bb853 Update @opencode-ai/plugin and SDK to v1.2.x and align system transform handler signature
- Bump @opencode-ai/plugin ^1.1.19 → ^1.2.16, @opencode-ai/sdk ^1.1.19 → ^1.2.17
- Update system-transform handler input type to match new plugin contract (optional sessionID, required model)
- Add @opencode-ai/sdk override in bun.lock

🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
2026-03-05 11:18:12 +09:00
YeonGyu-Kim
c8c99445ea fix(look-at): add catch block to prevent TUI crash on unexpected errors 2026-03-05 11:11:53 +09:00
YeonGyu-Kim
fc41a389c5 Merge pull request #2309 from code-yeongyu/fix/task-tui-session-metadata-sync
fix(task): align background delegate-task output with OpenCode TUI session metadata contract
2026-03-05 11:06:11 +09:00
YeonGyu-Kim
39d94a4af6 fix(task): disambiguate background task_id metadata 2026-03-05 11:02:49 +09:00
YeonGyu-Kim
acf4c46439 fix(task): align background output task_id with opencode contract 2026-03-05 11:02:49 +09:00
YeonGyu-Kim
5cbf7828f0 fix(task): avoid pending sessionId metadata in background delegate output 2026-03-05 11:02:49 +09:00
github-actions[bot]
0efd1b65bb @Romanok2805 has signed the CLA in code-yeongyu/oh-my-opencode#2306 2026-03-04 23:51:14 +00:00
github-actions[bot]
f8d2bd55b9 @RaviTharuma has signed the CLA in code-yeongyu/oh-my-opencode#2302 2026-03-04 21:53:50 +00:00
github-actions[bot]
1ef8d73ce5 @brandonwebb-vista has signed the CLA in code-yeongyu/oh-my-opencode#2299 2026-03-04 17:30:54 +00:00
github-actions[bot]
2b7524b1cb @guazi04 has signed the CLA in code-yeongyu/oh-my-opencode#2293 2026-03-04 10:31:56 +00:00
YeonGyu-Kim
d6b0e564bf feat(delegate-task): unify TUI metadata by adding model field to all 5 executor paths 2026-03-04 18:31:19 +09:00
github-actions[bot]
6897761b21 @SeeYouCowboi has signed the CLA in code-yeongyu/oh-my-opencode#2291 2026-03-04 08:50:49 +00:00
github-actions[bot]
fe66b68baa @chan1103 has signed the CLA in code-yeongyu/oh-my-opencode#2288 2026-03-04 08:41:04 +00:00
YeonGyu-Kim
a7f794c7a3 Merge pull request #2280 from code-yeongyu/feat/multimodal-looker-gpt53-codex-first
feat: make gpt-5.3-codex medium the primary model for multimodal-looker
2026-03-04 11:33:27 +09:00
YeonGyu-Kim
85690b69a8 test: update snapshots and assertions for kimi-k2.5-free removal 2026-03-04 11:33:11 +09:00
YeonGyu-Kim
8c2dcb75cb refactor: remove kimi-k2.5-free from all fallback chains and reorder multimodal-looker
kimi-k2.5-free is no longer available. Remove from all agent and category
fallback chains (sisyphus, multimodal-looker, prometheus, metis, atlas,
writing). Reorder multimodal-looker to: gpt-5.3-codex medium -> k2p5 ->
gemini-3-flash -> glm-4.6v -> gpt-5-nano.
2026-03-04 11:24:39 +09:00
YeonGyu-Kim
1ef5c17c35 feat: make gpt-5.3-codex medium the primary model for multimodal-looker
GPT-5.3 Codex has strong multimodal capabilities. Promote it to first
candidate in multimodal-looker fallback chain, with gemini-3-flash
following (matching the ULW pattern of gpt-5.3-codex -> gemini).
2026-03-04 11:20:55 +09:00
github-actions[bot]
42641a9922 @SwiggitySwerve has signed the CLA in code-yeongyu/oh-my-opencode#2277 2026-03-04 00:44:03 +00:00
github-actions[bot]
63b783ba72 @markarranz has signed the CLA in code-yeongyu/oh-my-opencode#2127 2026-03-03 14:12:10 +00:00
YeonGyu-Kim
840af692a0 chore: remove sisyphus-prompt.md
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
2026-03-03 21:43:12 +09:00
YeonGyu-Kim
2175d58f5d docs(readme): remove security warning banners and fix table formatting
Remove ohmyopencode.com impersonation warnings from all localized READMEs
and fix markdown table column alignment across ja, ko, ru, zh-cn variants.
Also remove Sisyphus Labs note block from ko README.

🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
2026-03-03 21:43:06 +09:00
github-actions[bot]
23e1a42690 @yhc509 has signed the CLA in code-yeongyu/oh-my-opencode#1455 2026-03-03 10:16:53 +00:00
github-actions[bot]
ceb8b239ac @janghoon-ju has signed the CLA in code-yeongyu/oh-my-opencode#2269 2026-03-03 07:44:39 +00:00
github-actions[bot]
6e57479ec1 @wangjingu has signed the CLA in code-yeongyu/oh-my-opencode#2265 2026-03-03 02:20:41 +00:00
github-actions[bot]
7fe2746e96 @ilovingjny has signed the CLA in code-yeongyu/oh-my-opencode#2259 2026-03-02 23:58:24 +00:00
github-actions[bot]
f983099957 @nous-labs has signed the CLA in code-yeongyu/oh-my-opencode#2254 2026-03-02 17:12:00 +00:00
YeonGyu-Kim
f9da00d021 Merge pull request #2251 from code-yeongyu/fix/pr-1906-image-conversion
fix(look-at): temp dir cleanup, Windows compat, argument injection prevention
2026-03-03 00:49:25 +09:00
YeonGyu-Kim
51a3d20dc9 Merge pull request #2250 from code-yeongyu/fix/pr-2113-model-fallback
fix(model-fallback): correct transform expectations and hermetic test isolation
2026-03-03 00:48:11 +09:00
YeonGyu-Kim
785dd529e1 Merge pull request #2249 from code-yeongyu/fix/pr-2173-timeout-issues
fix(delegate-task): resolve timeout handling regressions from #2173
2026-03-03 00:48:08 +09:00
YeonGyu-Kim
025d2a3579 Merge pull request #2248 from code-yeongyu/fix/pr-2080-model-format
fix: model format normalization and explicit config cache bypass
2026-03-03 00:48:04 +09:00
YeonGyu-Kim
0e858ee1df Merge pull request #2247 from code-yeongyu/fix/pr-1977-doctor-paths
fix(doctor): quote cache paths and respect release channel tags
2026-03-03 00:48:00 +09:00
YeonGyu-Kim
5ba9f37d8b Merge pull request #2246 from code-yeongyu/fix/pr-2166-notifier-fallback
fix(hooks): ensure notification fallback on terminal-notifier failure
2026-03-03 00:47:57 +09:00
YeonGyu-Kim
b5100d99df test(look-at): stabilize image-converter tests across platforms 2026-03-03 00:47:21 +09:00
YeonGyu-Kim
4123148376 fix(look-at): temp dir cleanup, Windows compat, argument injection prevention 2026-03-03 00:38:47 +09:00
YeonGyu-Kim
95fe698817 fix(model-fallback): correct transform expectations and hermetic test isolation 2026-03-03 00:38:45 +09:00
YeonGyu-Kim
031967857f fix(delegate-task): resolve timeout detection and config drift in sync poller 2026-03-03 00:38:22 +09:00
YeonGyu-Kim
c80a74c5f4 fix(model-resolution): normalize model format and remove dead config flag 2026-03-03 00:38:20 +09:00
YeonGyu-Kim
3d66a30406 fix(doctor): quote paths and respect version channels in fix messages 2026-03-03 00:38:19 +09:00
YeonGyu-Kim
cf40ca5553 fix(hooks): ensure notification fallback on terminal-notifier failure 2026-03-03 00:38:17 +09:00
YeonGyu-Kim
d4033da41a Merge pull request #2244 from code-yeongyu/fix/pr-2021-issues
fix(dispatch): resolve 3 bugs from PR #2021 plugin command wiring
2026-03-03 00:36:56 +09:00
YeonGyu-Kim
3363f0c63a fix(test): resolve test failure in PR #2021 fixes 2026-03-03 00:27:53 +09:00
YeonGyu-Kim
c084cc3f26 fix(dispatch): resolve plugin namespace parsing, template substitution, and discovery duplication 2026-03-03 00:14:01 +09:00
YeonGyu-Kim
f383d7abb5 Revert "Merge pull request #1951 from edxeth/feat/custom-agents"
This reverts commit 47e300b17e, reversing
changes made to 243ce1b7e8.
2026-03-02 23:55:48 +09:00
YeonGyu-Kim
33d39597ae docs(agents): regenerate AGENTS.md hierarchy with updated metrics and model configs
- 1208→1243 TS files (+35), 143k→155k LOC (+12k)
- Update all agent models: Sisyphus, Hephaestus, Oracle, Librarian, Atlas, Metis, Momus
- Add 6 new hook directories (39→45 dirs): beast-mode-system, hashline-edit-diff-enhancer, anthropic-image-context, task-reminder, compaction-todo-preserver, runtime-fallback
- Update category models: visual-engineering/artistry gemini-3-pro→gemini-3.1-pro
- Add 2 config schema files: fallback-models.ts, runtime-fallback.ts
- Timestamp: 2026-03-02 | Commit: 1c2caa09

🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
2026-03-02 23:40:38 +09:00
sisyphus-dev-ai
3d4269dcf9 chore: changes by sisyphus-dev-ai 2026-03-02 14:40:25 +00:00
YeonGyu-Kim
47e300b17e Merge pull request #1951 from edxeth/feat/custom-agents
feat(config): make custom agents first-class for planning and delegation
2026-03-02 23:28:57 +09:00
YeonGyu-Kim
243ce1b7e8 Merge pull request #1977 from iyoda/fix/doctor-cache-dir-fix-message
fix(doctor): point fix messages to actual OpenCode cache directory
2026-03-02 23:28:52 +09:00
YeonGyu-Kim
ddeb6e7c54 Merge pull request #1906 from xinpengdr/feat/auto-convert-heic-images
feat: Add automatic image format conversion for HEIC/RAW/PSD files
2026-03-02 23:28:45 +09:00
YeonGyu-Kim
e5d972cc2c Merge pull request #1984 from MoerAI/fix/respect-user-external-directory-permission
fix(config): respect user's external_directory permission setting
2026-03-02 23:28:22 +09:00
YeonGyu-Kim
7a43737cd6 fix(model-fallback): apply transformModelForProvider in getNextFallback
fix(model-fallback): apply transformModelForProvider in getNextFallback
2026-03-02 23:28:20 +09:00
YeonGyu-Kim
4905e6fc7c Merge pull request #2021 from cruzanstx/feat/marketplace-plugin-dispatch
feat(dispatch): wire marketplace plugin commands into slash command dispatch
2026-03-02 23:28:20 +09:00
YeonGyu-Kim
fdd806e729 Merge pull request #2173 from Lynricsy/feat/sync-poll-timeout-config
feat(delegate-task): ⚙️ make sync subagent timeout configurable via syncPollTimeoutMs
2026-03-02 23:28:15 +09:00
YeonGyu-Kim
8a16c95be1 Merge pull request #2080 from Firstbober/dev
fix: model format normalization and explicit config cache bypass
2026-03-02 23:28:06 +09:00
YeonGyu-Kim
8248381150 Merge pull request #2068 from DMax1314/patch-1
Update Kimi Code Subscription link in README
2026-03-02 23:28:03 +09:00
YeonGyu-Kim
0f6e9c7bfa Merge pull request #2125 from zhzy0077/fix/copilot-invalid-initiator
fix(chat-headers): skip x-initiator override for @ai-sdk/github-copilot models
2026-03-02 23:27:59 +09:00
YeonGyu-Kim
d43c5c68bd Merge pull request #2131 from maou-shonen/fix/comment-checker-dependency-version
fix(comment-checker): bump dependency to ^0.7.0 for --prompt support
2026-03-02 23:27:59 +09:00
YeonGyu-Kim
31f8493ee3 Merge pull request #2166 from 1noilimrev/fix/macos-notification-click-target
fix(hooks): use terminal-notifier for macOS notification click-to-focus
2026-03-02 23:27:57 +09:00
YeonGyu-Kim
8b57ca8c6c Merge pull request #2237 from iyoda/refactor/model-resolution-dedup
refactor(shared): deduplicate model resolution utility functions
2026-03-02 23:27:49 +09:00
YeonGyu-Kim
efa959895a Merge pull request #2198 from acamq/feat/no-auto-commit-work-plan
feat(start-work): add auto_commit config option
2026-03-02 23:27:44 +09:00
YeonGyu-Kim
36a29e826d Merge pull request #2184 from mertyldrm/fix/config-context-warning-leak
fix: remove config-context console.warn that leaks into TUI textbox
2026-03-02 23:27:43 +09:00
YeonGyu-Kim
7236e6ee02 Merge pull request #2176 from YLRong/fix/replace-pos-only-description
fix: remove misleading hint from replace pos only description
2026-03-02 23:27:41 +09:00
YeonGyu-Kim
50b9eddae9 fix: initialize config context in plugin runtime to prevent warnings
fix: initialize config context in plugin runtime to prevent warnings
2026-03-02 23:27:39 +09:00
YeonGyu-Kim
7df2a57efb Merge pull request #2193 from acamq/fix/load-mcp-hint
fix(skill_mcp): improve hint for builtin MCP names
2026-03-02 23:27:37 +09:00
YeonGyu-Kim
1c2caa09df fix(preemptive-compaction): allow re-compaction after context grows and use model-specific limits
compactedSessions permanently blocked re-compaction after first success,
causing unbounded context growth (e.g. 500k on Kimi K2.5 with 256k limit).

- Clear compactedSessions flag on new message.updated so compaction can
  re-trigger when context exceeds threshold again
- Use modelContextLimitsCache for model-specific context limits instead
  of always falling back to 200k for non-Anthropic providers
2026-03-02 23:07:39 +09:00
IYODA Atsushi
4b366926d4 refactor(shared): deduplicate model resolution utility functions
Extract normalizeModel() (3 identical copies) and normalizeModelID()
(2 identical copies) into canonical src/shared/model-normalization.ts.
Delete dead-end duplicate model-name-matcher.ts. Update all consumers.
2026-03-02 16:38:22 +09:00
acamq
5e726a2af2 fix(docs): remove corrupted text and duplicate entries in AGENTS.md
- Remove accidental '7ZB|' keystroke insertion on line 7
- Remove duplicate schema tree entries (start-work.ts and internal/permission.ts)
2026-02-27 13:45:35 -07:00
acamq
e2e3d110b7 feat(start-work): add auto_commit config option
Add start_work.auto_commit configuration option to allow users to
disable the automatic commit step in the /start-work workflow.

When auto_commit is false:
- STEP 8: COMMIT ATOMIC UNIT is removed from orchestrator reminder
- STEP 9: PROCEED TO NEXT TASK becomes STEP 8

Resolves #2197
2026-02-27 13:38:52 -07:00
acamq
deb904bbc4 fix(skill-mcp): clarify builtin MCP error hint 2026-02-27 10:03:20 -07:00
David Hardy
09fd131f24 fix: initialize config context in plugin runtime to prevent warnings
The auto-update checker hook calls getConfigDir() which requires the
config context to be initialized via initConfigContext(). When running
in the OpenCode TUI plugin runtime (vs CLI), this initialization never
happened, causing the warning:

"getConfigContext() called before initConfigContext(); defaulting to CLI paths."

This warning would appear when opening folders containing .mulch or .beads
directories because the lifecycle plugins triggered the auto-update checker.

Fix: Call initConfigContext("opencode", null) at plugin startup to ensure
the config context is properly initialized for all hooks and utilities.

Fixes upstream issue where TUI users see spurious bun install warnings.
2026-02-27 15:19:21 +00:00
Mert Yıldırım
83c024dd66 fix: remove console.warn that leaks into TUI textbox
getConfigContext() emitted a console.warn when called before
initConfigContext() completed. Since initConfigContext runs async
(spawns opencode --version subprocess), other modules calling
getConfigDir/getConfigJson could trigger this warning during startup.

The fallback behavior is intentional and safe (defaults to standard
CLI paths), but console.warn writes to stderr which the TUI captures,
causing the warning to render inside the user's textbox.

Fixes #2183
2026-02-27 13:51:51 +03:00
YLRong
c1eaf5fcab fix: remove misleading hint from replace pos-only description
The hint '(MOST COMMON for single-line edits)' misleads agents into
thinking pos-only replace is the default behavior. When agents want
to replace multiple lines but only specify pos without end, the tool
only replaces one line, causing duplicate code from retained lines.
2026-02-27 17:06:40 +08:00
Lynricsy
d09cf56e15 feat(delegate-task): ⚙️ make sync subagent timeout configurable via syncPollTimeoutMs
Allow users to set `background_task.syncPollTimeoutMs` in config to override
the default 10-minute sync subagent timeout. Affects sync task, sync continuation,
and unstable agent task paths. Minimum value: 60000ms (1 minute).

Co-authored-by: Wine Fox <fox@ling.plus>
2026-02-27 16:17:39 +08:00
1noilimrev
fbe3b5423d refactor(test): extract shared mock helper and add try-finally for env cleanup 2026-02-27 15:30:13 +09:00
1noilimrev
88bf8268f5 test(hooks): add darwin notification backend selection tests 2026-02-27 14:48:34 +09:00
1noilimrev
1c6d384f14 fix(hooks): use terminal-notifier for macOS notification click-to-focus 2026-02-27 14:39:07 +09:00
edxeth
d7ab5c4d7b refactor(schema): dedupe custom agent override with ref 2026-02-26 21:39:04 +01:00
edxeth
818fdc490c fix(config): avoid conflicting typo and migration guidance 2026-02-26 21:28:00 +01:00
edxeth
a5749a1392 fix(custom-agents): align planner catalog and schema validation 2026-02-26 21:14:00 +01:00
edxeth
922ff7f2bc docs(config): fix custom_agents examples 2026-02-26 20:55:58 +01:00
edxeth
da1e160add docs(config): document custom_agents behavior and delegation flow 2026-02-26 20:01:53 +01:00
edxeth
7e90c2c48f Merge remote-tracking branch 'origin/dev' into feat/custom-agents
# Conflicts:
#	src/agents/utils.test.ts
#	src/plugin-handlers/agent-config-handler.ts
2026-02-26 18:53:29 +01:00
maou shonen
acb51d1702 fix(comment-checker): bump dependency to ^0.7.0 for --prompt support 2026-02-26 09:48:57 +00:00
Zhiyuan Zheng
890a737d1e fix(chat-headers): skip x-initiator override for @ai-sdk/github-copilot models
OpenCode's copilot fetch wrapper already sets x-initiator based on the
actual HTTP request body content. When oh-my-opencode's chat.headers
hook overrides it with 'agent', the Copilot API detects a mismatch
between the header and the request body and rejects the request with
'invalid initiator'.

This matches the approach OpenCode's own chat.headers handler uses
(copilot.ts:314) — it explicitly skips @ai-sdk/github-copilot models
because the fetch wrapper handles x-initiator correctly on its own.
2026-02-26 12:38:05 +08:00
east-shine
94ff673d40 test(model-fallback): google provider 모델명 변환 테스트 추가
google provider에서 gemini-3-pro → gemini-3-pro-preview 변환이
getNextFallback를 통해 정상 적용되는지 검증하는 테스트 추가.
기존 github-copilot 테스트와 동일한 패턴으로 작성.
2026-02-25 21:40:28 +09:00
Jaden
f6d5f6f79f fix(model-fallback): apply transformModelForProvider in getNextFallback
The getNextFallback function returned raw model names from the
hardcoded fallback chain without transforming them for the target
provider. For example, github-copilot requires dot notation
(claude-sonnet-4.6) but the fallback chain stores hyphen notation
(claude-sonnet-4-6).

The background-agent retry handler already calls
transformModelForProvider correctly, but the sync chat.message
hook in model-fallback was missing it — a copy-paste omission.

Add transformModelForProvider call in getNextFallback and a test
verifying github-copilot model name transformation.
2026-02-25 17:15:13 +09:00
edxeth
8836b61aaa test(agents): stabilize provider gating and skill filter tests 2026-02-24 19:04:45 +01:00
edxeth
4f212dbaf9 chore(schema): regenerate schema after rebase conflict resolution 2026-02-24 18:49:34 +01:00
edxeth
fb139a7a01 fix(custom-agents): preserve summary flags during description merge 2026-02-24 18:46:49 +01:00
edxeth
754a2593f9 chore(schema): regenerate config schema after rebase 2026-02-24 18:46:49 +01:00
edxeth
ae12f2e9d2 feat(config): add custom_agents overrides and strict agent validation 2026-02-24 18:46:49 +01:00
MoerAI
8d66ab742b fix(test): update EventState inline literal to use createEventState() spread
EventState interface gained new required fields; the inline literal in the
session.status test was missing them, causing type errors and runtime failures.
2026-02-24 16:31:44 +09:00
MoerAI
ad79246376 fix(config): respect user's external_directory permission setting
applyToolConfig() forcibly overrode the user's external_directory
permission to 'allow' by placing OMO defaults after the user config
spread. Reorder so defaults come first and user config spreads on
top, allowing users to set 'ask' or 'deny'. The task permission
remains forced to 'deny' after the spread for security.

Closes #1973
2026-02-24 16:31:44 +09:00
Firstbober
13716f78aa fix: model format normalization and explicit config cache bypass
- Add normalizeModelFormat() utility for string/object model handling
- Update subagent-resolver to handle both model formats
- Add explicitUserConfig flag to ModelResolutionResult
- Set explicitUserConfig: true when user model is found in pipeline

This fixes the issue where plugin-provided models fail cache validation
and fall through to random fallback models.
2026-02-23 17:42:53 +01:00
Zhendong Li
584a82ea20 Update Kimi Code Subscription link in README
The link does not work anymore. You can use your referral link if you'd like. This one I'm sharing is just a direct link.
2026-02-23 01:59:43 -05:00
Gershom Rogers
0dee4377b8 feat(dispatch): wire marketplace plugin commands into slash command dispatch
Connect the existing plugin loader infrastructure to both slash command
dispatch paths (executor and slashcommand tool), enabling namespaced
commands like /daplug:run-prompt to resolve and execute.

- Add plugin discovery to executor.ts discoverAllCommands()
- Add plugin discovery to command-discovery.ts discoverCommandsSync()
- Add "plugin" to CommandScope type
- Remove blanket colon-rejection error (replaced with standard not-found)
- Update slash command regex to accept namespaced commands
- Thread claude_code.plugins config toggle through dispatch chain
- Add unit tests for plugin command discovery and dispatch

Closes #2019

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>
2026-02-21 10:05:50 -05:00
IYODA Atsushi
b94b193c21 fix(doctor): point fix messages to actual cache directory
The doctor's fix messages for outdated/mismatched plugin versions were
directing users to ~/.config/opencode with `bun update`, but OpenCode
loads plugins from its cache directory (~/.cache/opencode on Linux,
~/Library/Caches/opencode on macOS). Additionally, pinned versions in
the cache package.json make `bun update` a no-op — `bun add ...@latest`
is required.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-02-20 23:47:15 +09:00
XIN PENG
479bbb240f fix: avoid shell interpolation in image conversion commands 2026-02-17 08:58:41 -08:00
XIN PENG
814380b85c fix: normalize Base64 data URL input before image conversion 2026-02-17 08:21:07 -08:00
XIN PENG
ea814ffa15 fix: detect HEIC/HEIF from raw Base64 image signatures 2026-02-17 08:14:40 -08:00
XIN PENG
116ca090e0 fix: Add Base64 image format conversion support
Extends conversion logic to handle Base64-encoded images (e.g., from clipboard).
Previously, unsupported formats like HEIC/RAW/PSD in Base64 form bypassed
the conversion check and caused failures at multimodal-looker agent.

Changes:
- Add convertBase64ImageToJpeg() function in image-converter.ts
- Save Base64 data to temp file, convert, read back as Base64
- Update tools.ts to check and convert Base64 images when needed
- Ensure proper cleanup of all temporary files

Testing:
- All tests pass (29/29)
- Verified with 1.7MB HEIC file converted from Base64
- Type checking passes
2026-02-16 11:08:25 -08:00
XIN PENG
ae19ff60cf feat: Add automatic image format conversion for HEIC/RAW/PSD files
Adds automatic conversion of unsupported image formats (HEIC, HEIF, RAW, PSD)
to JPEG before sending to multimodal-looker agent.

Changes:
- Add image-converter.ts module with format detection and conversion
- Modify look_at tool to auto-convert unsupported formats
- Extend mime-type-inference.ts to support 15+ additional formats
- Use sips (macOS) and ImageMagick (Linux/Windows) for conversion
- Add proper cleanup of temporary files

Fixes #722

Testing:
- All existing tests pass (29/29)
- TypeScript type checking passes
- Verified HEIC to JPEG conversion on macOS
2026-02-16 10:50:05 -08:00
162 changed files with 7525 additions and 1373 deletions

View File

View File

@@ -1,10 +1,10 @@
# oh-my-opencode — OpenCode Plugin
**Generated:** 2026-02-24 | **Commit:** fcb90d92 | **Branch:** dev
**Generated:** 2026-03-02 | **Commit:** 1c2caa09 | **Branch:** dev
## OVERVIEW
OpenCode plugin (npm: `oh-my-opencode`) that extends Claude Code (OpenCode fork) with multi-agent orchestration, 46 lifecycle hooks, 26 tools, skill/command/MCP systems, and Claude Code compatibility. 1208 TypeScript files, 143k LOC.
OpenCode plugin (npm: `oh-my-opencode`) that extends Claude Code (OpenCode fork) with multi-agent orchestration, 46 lifecycle hooks, 26 tools, skill/command/MCP systems, and Claude Code compatibility. 1243 TypeScript files, 155k LOC.
## STRUCTURE
@@ -14,16 +14,16 @@ oh-my-opencode/
│ ├── index.ts # Plugin entry: loadConfig → createManagers → createTools → createHooks → createPluginInterface
│ ├── plugin-config.ts # JSONC multi-level config: user → project → defaults (Zod v4)
│ ├── agents/ # 11 agents (Sisyphus, Hephaestus, Oracle, Librarian, Explore, Atlas, Prometheus, Metis, Momus, Multimodal-Looker, Sisyphus-Junior)
| `hooks/`                # 46 hooks across 39 directories + 6 standalone files
│ ├── hooks/ # 46 hooks across 45 directories + 11 standalone files
│ ├── tools/ # 26 tools across 15 directories
│ ├── features/ # 19 feature modules (background-agent, skill-loader, tmux, MCP-OAuth, etc.)
│ ├── shared/ # 100+ utility files in 13 categories
│ ├── config/ # Zod v4 schema system (22+ files)
│ ├── shared/ # 95+ utility files in 13 categories
│ ├── config/ # Zod v4 schema system (24 files)
│ ├── cli/ # CLI: install, run, doctor, mcp-oauth (Commander.js)
│ ├── mcp/ # 3 built-in remote MCPs (websearch, context7, grep_app)
│ ├── plugin/ # 8 OpenCode hook handlers + 46 hook composition
│ └── plugin-handlers/ # 6-phase config loading pipeline
├── packages/ # Monorepo: comment-checker, opencode-sdk, 10 platform binaries
├── packages/ # Monorepo: cli-runner, 12 platform binaries
└── local-ignore/ # Dev-only test fixtures
```
@@ -123,7 +123,7 @@ bunx oh-my-opencode run # Non-interactive session
|----------|---------|---------|
| ci.yml | push/PR | Tests (split: mock-heavy isolated + batch), typecheck, build, schema auto-commit |
| publish.yml | manual | Version bump, npm publish, platform binaries, GitHub release, merge to dev |
| publish-platform.yml | called | 11 platform binaries via bun compile (darwin/linux/windows) |
| publish-platform.yml | called | 12 platform binaries via bun compile (darwin/linux/windows) |
| sisyphus-agent.yml | @mention | AI agent handles issues/PRs |
## NOTES

View File

@@ -1,14 +1,3 @@
> [!WARNING]
> **セキュリティ警告: 偽装サイトにご注意ください**
>
> **ohmyopencode.com はこのプロジェクトとは一切関係がありません。** 私たちはそのサイトを運営したり承認したりしていません。
>
> OhMyOpenCodeは**無料かつオープンソース**です。「公式」を名乗る第三者のサイトからインストーラーをダウンロードしたり、支払い情報を入力したり**しないでください。**
>
> 偽装サイトはペイウォールの背後に隠れており、**どのような悪意あるプログラムを配布しているか検証できません**。そこからのダウンロードはすべて**潜在的に危険**であると見なしてください。
>
> ✅ 公式ダウンロード: https://github.com/code-yeongyu/oh-my-opencode/releases
> [!NOTE]
>
> [![Sisyphus Labs - Sisyphus is the agent that codes like your team.](./.github/assets/sisyphuslabs.png?v=2)](https://sisyphuslabs.ai)
@@ -135,23 +124,23 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
- [GLM Coding プラン ($10)](https://z.ai/subscribe)
- 従量課金pay-per-tokenの対象であれば、kimiやgeminiモデルを使っても費用はほとんどかかりません。
| | 機能 | 何をするのか |
| :---: | :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **規律あるエージェント (Discipline Agents)** | Sisyphusが Hephaestus、Oracle、Librarian、Exploreをオーケストレーションします。完全なAI開発チームが並列で動きます。 |
| ⚡ | **`ultrawork` / `ulw`** | 一言でOK。すべてのエージェントがアクティブになり、終わるまで止まりません。 |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | ユーザーの真の意図を分析してから分類・行動します。もう文字通りに誤解して的外れなことをすることはありません。 |
| 🔗 | **ハッシュベースの編集ツール** | `LINE#ID` のコンテンツハッシュですべての変更を検証します。stale-lineエラー0%。[oh-my-pi](https://github.com/can1357/oh-my-pi)にインスパイアされています。[ハーネス問題 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | ワークスペース単位のリネーム、ビルド前の診断、ASTを考慮した書き換え。エージェントにIDEレベルの精度を提供します。 |
| 🧠 | **バックグラウンドエージェント** | 5人以上の専門家を並列で投入します。コンテキストは軽く保ち、結果は準備ができ次第受け取ります。 |
| 📚 | **組み込みMCP** | ExaWeb検索、Context7公式ドキュメント、Grep.appGitHub検索。常にオンです。 |
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自己参照ループ。100%完了するまで絶対に止まりません。 |
| ✅ | **Todoの強制執行** | エージェントがサボる?システムが首根っこを掴んで戻します。あなたのタスクは必ず終わります。 |
| 💬 | **コメントチェッカー** | コメントからAI臭い無駄話を排除します。シニアエンジニアが書いたようなコードになります。 |
| 🖥️ | **Tmux統合** | 完全なインタラクティブターミナル。REPL、デバッガー、TUIアプリがすべてリアルタイムで動きます。 |
| 🔌 | **Claude Code互換性** | 既存のフック、コマンド、スキル、MCP、プラグインすべてここでそのまま動きます。 |
| 🎯 | **スキル内蔵MCP** | スキルが独自のMCPサーバーを持ち歩きます。コンテキストが肥大化しません。 |
| 📋 | **Prometheusプランナー** | インタビューモードで、コードを1行触る前に戦略的な計画から立てます。 |
| 🔍 | **`/init-deep`** | プロジェクト全体にわたって階層的な `AGENTS.md` ファイルを自動生成します。トークン効率とエージェントのパフォーマンスの両方を向上させます。 |
| | 機能 | 何をするのか |
| :---: | :------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **規律あるエージェント (Discipline Agents)** | Sisyphusが Hephaestus、Oracle、Librarian、Exploreをオーケストレーションします。完全なAI開発チームが並列で動きます。 |
| ⚡ | **`ultrawork` / `ulw`** | 一言でOK。すべてのエージェントがアクティブになり、終わるまで止まりません。 |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | ユーザーの真の意図を分析してから分類・行動します。もう文字通りに誤解して的外れなことをすることはありません。 |
| 🔗 | **ハッシュベースの編集ツール** | `LINE#ID` のコンテンツハッシュですべての変更を検証します。stale-lineエラー0%。[oh-my-pi](https://github.com/can1357/oh-my-pi)にインスパイアされています。[ハーネス問題 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | ワークスペース単位のリネーム、ビルド前の診断、ASTを考慮した書き換え。エージェントにIDEレベルの精度を提供します。 |
| 🧠 | **バックグラウンドエージェント** | 5人以上の専門家を並列で投入します。コンテキストは軽く保ち、結果は準備ができ次第受け取ります。 |
| 📚 | **組み込みMCP** | ExaWeb検索、Context7公式ドキュメント、Grep.appGitHub検索。常にオンです。 |
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自己参照ループ。100%完了するまで絶対に止まりません。 |
| ✅ | **Todoの強制執行** | エージェントがサボる?システムが首根っこを掴んで戻します。あなたのタスクは必ず終わります。 |
| 💬 | **コメントチェッカー** | コメントからAI臭い無駄話を排除します。シニアエンジニアが書いたようなコードになります。 |
| 🖥️ | **Tmux統合** | 完全なインタラクティブターミナル。REPL、デバッガー、TUIアプリがすべてリアルタイムで動きます。 |
| 🔌 | **Claude Code互換性** | 既存のフック、コマンド、スキル、MCP、プラグインすべてここでそのまま動きます。 |
| 🎯 | **スキル内蔵MCP** | スキルが独自のMCPサーバーを持ち歩きます。コンテキストが肥大化しません。 |
| 📋 | **Prometheusプランナー** | インタビューモードで、コードを1行触る前に戦略的な計画から立てます。 |
| 🔍 | **`/init-deep`** | プロジェクト全体にわたって階層的な `AGENTS.md` ファイルを自動生成します。トークン効率とエージェントのパフォーマンスの両方を向上させます。 |
### 規律あるエージェント (Discipline Agents)
@@ -176,11 +165,11 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
Sisyphusがサブエージェントにタスクを委任する際、モデルを直接選ぶことはありません。**カテゴリー**を選びます。カテゴリーは自動的に適切なモデルにマッピングされます:
| カテゴリー | 用途 |
| :------------------- | :--------------------------------- |
| `visual-engineering` | フロントエンド、UI/UX、デザイン |
| `deep` | 自律的なリサーチと実行 |
| `quick` | 単一ファイルの変更、タイポの修正 |
| カテゴリー | 用途 |
| :------------------- | :----------------------------------- |
| `visual-engineering` | フロントエンド、UI/UX、デザイン |
| `deep` | 自律的なリサーチと実行 |
| `quick` | 単一ファイルの変更、タイポの修正 |
| `ultrabrain` | ハードロジック、アーキテクチャの決定 |
エージェントがどのような種類の作業かを伝え、ハーネスが適切なモデルを選択します。あなたは何も触る必要はありません。

View File

@@ -1,19 +1,3 @@
> [!WARNING]
> **보안 경고: 사칭 사이트 주의**
>
> **ohmyopencode.com은 이 프로젝트와 아무런 관련이 없습니다.** 우리는 해당 사이트를 운영하거나 보증하지 않습니다.
>
> OhMyOpenCode는 **무료 오픈소스**입니다. "공식"을 사칭하는 제3자 사이트에서 인스톨러를 다운로드하거나 결제 정보를 입력하지 **마세요.**
>
> 사칭 사이트는 페이월 뒤에 숨어 있어 **어떤 악성 코드를 배포하는지 확인할 수 없습니다**. 해당 사이트의 다운로드는 모두 **잠재적 위험**으로 간주하세요.
>
> ✅ 공식 다운로드: https://github.com/code-yeongyu/oh-my-opencode/releases
> [!NOTE]
>
> [![Sisyphus Labs - Sisyphus is the agent that codes like your team.](./.github/assets/sisyphuslabs.png?v=2)](https://sisyphuslabs.ai)
> > **우리는 프론티어 에이전트의 미래를 정의하기 위해 Sisyphus의 완벽한 프로덕트 버전을 만들고 있습니다. <br />[여기](https://sisyphuslabs.ai)에서 대기자 명단에 등록하세요.**
> [!TIP]
> 저희와 함께 하세요!
>
@@ -134,23 +118,23 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
- [GLM Coding 요금제 ($10)](https://z.ai/subscribe)
- 종량제(pay-per-token) 대상자라면 kimi와 gemini 모델을 써도 비용이 별로 안 나옵니다.
| | 기능 | 역할 |
| :---: | :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **기강 잡힌 에이전트 (Discipline Agents)** | Sisyphus가 Hephaestus, Oracle, Librarian, Explore를 오케스트레이션합니다. 완전한 AI 개발팀이 병렬로 돌아갑니다. |
| ⚡ | **`ultrawork` / `ulw`** | 단어 하나면 됩니다. 모든 에이전트가 활성화되고 다 끝날 때까지 멈추지 않습니다. |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | 사용자의 진짜 의도를 분석한 뒤 분류하거나 행동합니다. 더 이상 문자 그대로 오해해서 헛짓거리하는 일이 없습니다. |
| 🔗 | **해시 기반 편집 툴** | `LINE#ID` 콘텐츠 해시로 모든 변경 사항을 검증합니다. stale-line 에러 0%. [oh-my-pi](https://github.com/can1357/oh-my-pi)에서 영감을 받았습니다. [하니스 프로블러 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | 워크스페이스 단위 이름 변경, 빌드 전 진단, AST 기반 재작성. 에이전트에게 IDE급 정밀도를 제공합니다. |
| 🧠 | **백그라운드 에이전트** | 5명 이상의 전문가를 병렬로 투입합니다. 컨텍스트는 가볍게 유지하고 결과는 준비될 때 받습니다. |
| 📚 | **기본 내장 MCP** | Exa(웹 검색), Context7(공식 문서), Grep.app(GitHub 검색). 항상 켜져 있습니다. |
| 🔁 | **Ralph Loop / `/ulw-loop`** | 자기 참조 루프. 100% 완료될 때까지 절대 멈추지 않습니다. |
| ✅ | **Todo 강제 집행** | 에이전트가 딴짓한다고요? 시스템이 멱살 잡고 끌고 옵니다. 당신의 작업은 무조건 끝납니다. |
| 💬 | **주석 검사기** | 주석에 AI 냄새나는 헛소리를 빼버립니다. 시니어 개발자가 짠 것 같은 코드가 됩니다. |
| 🖥️ | **Tmux 연동** | 완전한 인터랙티브 터미널. REPL, 디버거, TUI 앱들 모두 실시간으로 돌아갑니다. |
| 🔌 | **Claude Code 호환성** | 기존 훅, 명령어, 스킬, MCP, 플러그인? 전부 여기서 그대로 돌아갑니다. |
| 🎯 | **스킬 내장 MCP** | 스킬이 자기만의 MCP 서버를 들고 다닙니다. 컨텍스트가 부풀어 오르지 않습니다. |
| 📋 | **Prometheus 플래너** | 인터뷰 모드로 코드 한 줄 만지기 전에 전략적인 계획부터 세웁니다. |
| 🔍 | **`/init-deep`** | 프로젝트 전체에 걸쳐 계층적인 `AGENTS.md` 파일을 자동 생성합니다. 토큰 효율과 에이전트 성능 둘 다 잡습니다. |
| | 기능 | 역할 |
| :---: | :------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **기강 잡힌 에이전트 (Discipline Agents)** | Sisyphus가 Hephaestus, Oracle, Librarian, Explore를 오케스트레이션합니다. 완전한 AI 개발팀이 병렬로 돌아갑니다. |
| ⚡ | **`ultrawork` / `ulw`** | 단어 하나면 됩니다. 모든 에이전트가 활성화되고 다 끝날 때까지 멈추지 않습니다. |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | 사용자의 진짜 의도를 분석한 뒤 분류하거나 행동합니다. 더 이상 문자 그대로 오해해서 헛짓거리하는 일이 없습니다. |
| 🔗 | **해시 기반 편집 툴** | `LINE#ID` 콘텐츠 해시로 모든 변경 사항을 검증합니다. stale-line 에러 0%. [oh-my-pi](https://github.com/can1357/oh-my-pi)에서 영감을 받았습니다. [하니스 프로블러 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | 워크스페이스 단위 이름 변경, 빌드 전 진단, AST 기반 재작성. 에이전트에게 IDE급 정밀도를 제공합니다. |
| 🧠 | **백그라운드 에이전트** | 5명 이상의 전문가를 병렬로 투입합니다. 컨텍스트는 가볍게 유지하고 결과는 준비될 때 받습니다. |
| 📚 | **기본 내장 MCP** | Exa(웹 검색), Context7(공식 문서), Grep.app(GitHub 검색). 항상 켜져 있습니다. |
| 🔁 | **Ralph Loop / `/ulw-loop`** | 자기 참조 루프. 100% 완료될 때까지 절대 멈추지 않습니다. |
| ✅ | **Todo 강제 집행** | 에이전트가 딴짓한다고요? 시스템이 멱살 잡고 끌고 옵니다. 당신의 작업은 무조건 끝납니다. |
| 💬 | **주석 검사기** | 주석에 AI 냄새나는 헛소리를 빼버립니다. 시니어 개발자가 짠 것 같은 코드가 됩니다. |
| 🖥️ | **Tmux 연동** | 완전한 인터랙티브 터미널. REPL, 디버거, TUI 앱들 모두 실시간으로 돌아갑니다. |
| 🔌 | **Claude Code 호환성** | 기존 훅, 명령어, 스킬, MCP, 플러그인? 전부 여기서 그대로 돌아갑니다. |
| 🎯 | **스킬 내장 MCP** | 스킬이 자기만의 MCP 서버를 들고 다닙니다. 컨텍스트가 부풀어 오르지 않습니다. |
| 📋 | **Prometheus 플래너** | 인터뷰 모드로 코드 한 줄 만지기 전에 전략적인 계획부터 세웁니다. |
| 🔍 | **`/init-deep`** | 프로젝트 전체에 걸쳐 계층적인 `AGENTS.md` 파일을 자동 생성합니다. 토큰 효율과 에이전트 성능 둘 다 잡습니다. |
### 기강 잡힌 에이전트 (Discipline Agents)
@@ -176,11 +160,11 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
Sisyphus가 하위 에이전트에게 일을 맡길 때, 모델을 직접 고르지 않습니다. **카테고리**를 고릅니다. 카테고리는 자동으로 올바른 모델에 매핑됩니다:
| 카테고리 | 용도 |
| :------------------- | :--------------------------------- |
| `visual-engineering` | 프론트엔드, UI/UX, 디자인 |
| `deep` | 자율 리서치 및 실행 |
| `quick` | 단일 파일 변경, 오타 수정 |
| `ultrabrain` | 하드 로직, 아키텍처 결정 |
| :------------------- | :------------------------ |
| `visual-engineering` | 프론트엔드, UI/UX, 디자인 |
| `deep` | 자율 리서치 및 실행 |
| `quick` | 단일 파일 변경, 오타 수정 |
| `ultrabrain` | 하드 로직, 아키텍처 결정 |
에이전트가 어떤 작업인지 말하면, 하네스가 알아서 적합한 모델을 꺼내옵니다. 당신은 손댈 게 없습니다.

View File

@@ -1,14 +1,3 @@
> [!WARNING]
> **Security warning: impersonation site**
>
> **ohmyopencode.com is NOT affiliated with this project.** We do not operate or endorse that site.
>
> OhMyOpenCode is **free and open-source**. Do **not** download installers or enter payment details on third-party sites that claim to be "official."
>
> Because the impersonation site is behind a paywall, we **cannot verify what it distributes**. Treat any downloads from it as **potentially unsafe**.
>
> ✅ Official downloads: https://github.com/code-yeongyu/oh-my-opencode/releases
> [!NOTE]
>
> [![Sisyphus Labs - Sisyphus is the agent that codes like your team.](./.github/assets/sisyphuslabs.png?v=2)](https://sisyphuslabs.ai)
@@ -134,7 +123,7 @@ Everything below, every feature, every optimization, you don't need to know it.
Even only with following subscriptions, ultrawork will work well (this project is not affiliated, this is just personal recommendation):
- [ChatGPT Subscription ($20)](https://chatgpt.com/)
- [Kimi Code Subscription ($0.99) (*only this month)](https://www.kimi.com/membership/pricing?track_id=5cdeca93-66f0-4d35-aabb-b6df8fcea328)
- [Kimi Code Subscription ($0.99) (*only this month)](https://www.kimi.com/kimiplus/sale)
- [GLM Coding Plan ($10)](https://z.ai/subscribe)
- If you are eligible for pay-per-token, using kimi and gemini models won't cost you that much.

View File

@@ -1,13 +1,3 @@
> [!WARNING] **Предупреждение о безопасности: сайт-имитатор**
>
> **ohmyopencode.com НЕ аффилирован с этим проектом.** Мы не управляем этим сайтом и не одобряем его.
>
> OhMyOpenCode — **бесплатный и открытый проект**. Не скачивайте установщики и не вводите платёжные данные на сторонних сайтах, которые называют себя «официальными».
>
> Поскольку сайт-имитатор находится за платным доступом, мы **не можем проверить, что именно он распространяет**. Относитесь к любым загрузкам с него как к **потенциально небезопасным**.
>
> ✅ Официальные загрузки: https://github.com/code-yeongyu/oh-my-opencode/releases
> [!NOTE]
>
> [![Sisyphus Labs - Sisyphus is the agent that codes like your team.](./.github/assets/sisyphuslabs.png?v=2)](https://sisyphuslabs.ai)
@@ -121,23 +111,23 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
- [Тариф GLM Coding ($10)](https://z.ai/subscribe)
- При доступе к оплате за токены использование моделей Kimi и Gemini обойдётся недорого.
| | Функция | Что делает |
| ---- | -------------------------------------------------------- | ------------------------------------------------------------ |
| 🤖 | **Дисциплинированные агенты** | Sisyphus оркестрирует Hephaestus, Oracle, Librarian, Explore. Полноценная AI-команда разработки в параллельном режиме. |
| ⚡ | **`ultrawork` / `ulw`** | Одно слово. Все агенты активируются. Не останавливается, пока задача не выполнена. |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Анализирует истинное намерение пользователя перед классификацией и действием. Никакого буквального неверного толкования. |
| 🔗 | **Инструмент правок на основе хэш-якорей** | Хэш содержимого `LINE#ID` проверяет каждое изменение. Ноль ошибок с устаревшими строками. Вдохновлено [oh-my-pi](https://github.com/can1357/oh-my-pi). [Проблема обвязки →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | Переименование в рабочем пространстве, диагностика перед сборкой, переписывание с учётом AST. Точность IDE для агентов. |
| 🧠 | **Фоновые агенты** | Запускайте 5+ специалистов параллельно. Контекст остаётся компактным. Результаты — когда готовы. |
| 📚 | **Встроенные MCP** | Exa (веб-поиск), Context7 (официальная документация), Grep.app (поиск по GitHub). Всегда включены. |
| 🔁 | **Ralph Loop / `/ulw-loop`** | Самореферентный цикл. Не останавливается, пока задача не выполнена на 100%. |
| ✅ | **Todo Enforcer** | Агент завис? Система немедленно возвращает его в работу. Ваша задача будет выполнена, точка. |
| 💬 | **Comment Checker** | Никакого AI-мусора в комментариях. Код читается так, словно его писал опытный разработчик. |
| 🖥️ | **Интеграция с Tmux** | Полноценный интерактивный терминал. REPL, дебаггеры, TUI. Всё живое. |
| 🔌 | **Совместимость с Claude Code** | Ваши хуки, команды, навыки, MCP и плагины? Всё работает без изменений. |
| 🎯 | **MCP, встроенные в навыки** | Навыки несут собственные MCP-серверы. Никакого раздувания контекста. |
| 📋 | **Prometheus Planner** | Стратегическое планирование в режиме интервью перед любым выполнением. |
| 🔍 | **`/init-deep`** | Автоматически генерирует иерархические файлы `AGENTS.md` по всему проекту. Отлично работает на эффективность токенов и производительность агента. |
| | Функция | Что делает |
| --- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **Дисциплинированные агенты** | Sisyphus оркестрирует Hephaestus, Oracle, Librarian, Explore. Полноценная AI-команда разработки в параллельном режиме. |
| ⚡ | **`ultrawork` / `ulw`** | Одно слово. Все агенты активируются. Не останавливается, пока задача не выполнена. |
| 🚪 | **[IntentGate](https://factory.ai/news/terminal-bench)** | Анализирует истинное намерение пользователя перед классификацией и действием. Никакого буквального неверного толкования. |
| 🔗 | **Инструмент правок на основе хэш-якорей** | Хэш содержимого `LINE#ID` проверяет каждое изменение. Ноль ошибок с устаревшими строками. Вдохновлено [oh-my-pi](https://github.com/can1357/oh-my-pi). [Проблема обвязки →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | Переименование в рабочем пространстве, диагностика перед сборкой, переписывание с учётом AST. Точность IDE для агентов. |
| 🧠 | **Фоновые агенты** | Запускайте 5+ специалистов параллельно. Контекст остаётся компактным. Результаты — когда готовы. |
| 📚 | **Встроенные MCP** | Exa (веб-поиск), Context7 (официальная документация), Grep.app (поиск по GitHub). Всегда включены. |
| 🔁 | **Ralph Loop / `/ulw-loop`** | Самореферентный цикл. Не останавливается, пока задача не выполнена на 100%. |
| ✅ | **Todo Enforcer** | Агент завис? Система немедленно возвращает его в работу. Ваша задача будет выполнена, точка. |
| 💬 | **Comment Checker** | Никакого AI-мусора в комментариях. Код читается так, словно его писал опытный разработчик. |
| 🖥️ | **Интеграция с Tmux** | Полноценный интерактивный терминал. REPL, дебаггеры, TUI. Всё живое. |
| 🔌 | **Совместимость с Claude Code** | Ваши хуки, команды, навыки, MCP и плагины? Всё работает без изменений. |
| 🎯 | **MCP, встроенные в навыки** | Навыки несут собственные MCP-серверы. Никакого раздувания контекста. |
| 📋 | **Prometheus Planner** | Стратегическое планирование в режиме интервью перед любым выполнением. |
| 🔍 | **`/init-deep`** | Автоматически генерирует иерархические файлы `AGENTS.md` по всему проекту. Отлично работает на эффективность токенов и производительность агента. |
### Дисциплинированные агенты
@@ -268,7 +258,7 @@ project/
```bash
# Удалить пользовательский конфиг
rm -f ~/.config/opencode/oh-my-opencode.json ~/.config/opencode/oh-my-opencode.jsonc
# Удалить конфиг проекта (если существует)
rm -f .opencode/oh-my-opencode.json .opencode/oh-my-opencode.jsonc
```

View File

@@ -1,14 +1,3 @@
> [!WARNING]
> **安全警告:注意假冒网站**
>
> **ohmyopencode.com 与本项目没有任何关系。** 我们不运营也不认可该网站。
>
> OhMyOpenCode 是**免费且开源的**。**不要**从自称“官方”的第三方网站下载安装程序或输入付款信息。
>
> 假冒网站隐藏在付费墙后,我们**无法验证它分发的内容**。将其所有下载视为**潜在危险**。
>
> ✅ 官方下载地址https://github.com/code-yeongyu/oh-my-opencode/releases
> [!NOTE]
>
> [![Sisyphus Labs - Sisyphus is the agent that codes like your team.](./.github/assets/sisyphuslabs.png?v=2)](https://sisyphuslabs.ai)
@@ -136,23 +125,23 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
- [GLM Coding 套餐 ($10)](https://z.ai/subscribe)
- 如果你能使用按 token 计费的方式,用 kimi 和 gemini 模型花不了多少钱。
| | 特性 | 功能说明 |
| :---: | :--------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- |
| 🤖 | **自律军团 (Discipline Agents)** | Sisyphus 负责调度 Hephaestus、Oracle、Librarian 和 Explore。一支完整的 AI 开发团队并行工作。 |
| ⚡ | **`ultrawork` / `ulw`** | 一键触发,所有智能体出动。任务完成前绝不罢休。 |
| 🚪 | **[IntentGate 意图门](https://factory.ai/news/terminal-bench)** | 真正行动前,先分析用户的真实意图。彻底告别被字面意思误导的 AI 废话。 |
| 🔗 | **基于哈希的编辑工具** | 每次修改都通过 `LINE#ID` 内容哈希验证、0% 错误修改。灵感来自 [oh-my-pi](https://github.com/can1357/oh-my-pi)。[马具问题 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | 工作区级别的重命名、构建前诊断、基于 AST 的重写。为 Agent 提供 IDE 级别的精度。 |
| 🧠 | **后台智能体** | 同时发射 5+ 个专家并行工作。保持上下文干净,随时获取成果。 |
| 📚 | **内置 MCP** | Exa (网络搜索)、Context7 (官方文档)、Grep.app (GitHub 源码搜索)。默认开启。 |
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自我引用闭环。达不到 100% 完成度绝不停止。 |
| ✅ | **Todo 强制执行** | Agent 想要摸鱼?系统直接揪着领子拽回来。你的任务,必须完成。 |
| 💬 | **注释审查员** | 剔除带有浓烈 AI 味的冗余注释。写出的代码就像老练的高级工程师写的。 |
| 🖥️ | **Tmux 集成** | 完整的交互式终端支持。跑 REPL、用调试器、用 TUI 工具,全都在实时会话中完成。 |
| 🔌 | **Claude Code 兼容** | 你现有的 Hooks、命令、技能、MCP 和插件?全都能无缝迁移过来。 |
| 🎯 | **技能内嵌 MCP** | 技能自带其所需的 MCP 服务器。按需开启,不会撑爆你的上下文窗口。 |
| 📋 | **Prometheus 规划师** | 动手写代码前,先通过访谈模式做好战略规划。 |
| 🔍 | **`/init-deep`** | 在整个项目目录层级中自动生成 `AGENTS.md`。不仅省 Token还能大幅提升 Agent 理解力。 |
| | 特性 | 功能说明 |
| :---: | :-------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 🤖 | **自律军团 (Discipline Agents)** | Sisyphus 负责调度 Hephaestus、Oracle、Librarian 和 Explore。一支完整的 AI 开发团队并行工作。 |
| ⚡ | **`ultrawork` / `ulw`** | 一键触发,所有智能体出动。任务完成前绝不罢休。 |
| 🚪 | **[IntentGate 意图门](https://factory.ai/news/terminal-bench)** | 真正行动前,先分析用户的真实意图。彻底告别被字面意思误导的 AI 废话。 |
| 🔗 | **基于哈希的编辑工具** | 每次修改都通过 `LINE#ID` 内容哈希验证、0% 错误修改。灵感来自 [oh-my-pi](https://github.com/can1357/oh-my-pi)。[马具问题 →](https://blog.can.ac/2026/02/12/the-harness-problem/) |
| 🛠️ | **LSP + AST-Grep** | 工作区级别的重命名、构建前诊断、基于 AST 的重写。为 Agent 提供 IDE 级别的精度。 |
| 🧠 | **后台智能体** | 同时发射 5+ 个专家并行工作。保持上下文干净,随时获取成果。 |
| 📚 | **内置 MCP** | Exa (网络搜索)、Context7 (官方文档)、Grep.app (GitHub 源码搜索)。默认开启。 |
| 🔁 | **Ralph Loop / `/ulw-loop`** | 自我引用闭环。达不到 100% 完成度绝不停止。 |
| ✅ | **Todo 强制执行** | Agent 想要摸鱼?系统直接揪着领子拽回来。你的任务,必须完成。 |
| 💬 | **注释审查员** | 剔除带有浓烈 AI 味的冗余注释。写出的代码就像老练的高级工程师写的。 |
| 🖥️ | **Tmux 集成** | 完整的交互式终端支持。跑 REPL、用调试器、用 TUI 工具,全都在实时会话中完成。 |
| 🔌 | **Claude Code 兼容** | 你现有的 Hooks、命令、技能、MCP 和插件?全都能无缝迁移过来。 |
| 🎯 | **技能内嵌 MCP** | 技能自带其所需的 MCP 服务器。按需开启,不会撑爆你的上下文窗口。 |
| 📋 | **Prometheus 规划师** | 动手写代码前,先通过访谈模式做好战略规划。 |
| 🔍 | **`/init-deep`** | 在整个项目目录层级中自动生成 `AGENTS.md`。不仅省 Token还能大幅提升 Agent 理解力。 |
### 自律军团 (Discipline Agents)
@@ -177,11 +166,11 @@ Read this and tell me why it's not just another boilerplate: https://raw.githubu
当 Sisyphus 把任务分配给子智能体时,他选择的不是具体的模型,而是 **类别 (Category)**。系统会自动将类别映射到最合适的模型:
| 类别 | 作用领域 |
| :------------------- | :--------------------------------- |
| `visual-engineering` | 前端、UI/UX、设计 |
| `deep` | 深度自主调研与执行 |
| `quick` | 单文件修改、修错字 |
| 类别 | 作用领域 |
| :------------------- | :--------------------- |
| `visual-engineering` | 前端、UI/UX、设计 |
| `deep` | 深度自主调研与执行 |
| `quick` | 单文件修改、修错字 |
| `ultrabrain` | 复杂硬核逻辑、架构决策 |
智能体只需要说明要做什么类型的工作,框架就会挑选出最合适的模型去干。你完全不需要操心。

View File

@@ -3685,6 +3685,10 @@
"messageStalenessTimeoutMs": {
"type": "number",
"minimum": 60000
},
"syncPollTimeoutMs": {
"type": "number",
"minimum": 60000
}
},
"additionalProperties": false
@@ -3837,6 +3841,19 @@
},
"additionalProperties": false
},
"start_work": {
"type": "object",
"properties": {
"auto_commit": {
"default": true,
"type": "boolean"
}
},
"required": [
"auto_commit"
],
"additionalProperties": false
},
"_migrations": {
"type": "array",
"items": {

119
bun.lock
View File

@@ -1,6 +1,6 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"configVersion": 1,
"workspaces": {
"": {
"name": "oh-my-opencode",
@@ -8,10 +8,10 @@
"@ast-grep/cli": "^0.40.0",
"@ast-grep/napi": "^0.40.0",
"@clack/prompts": "^0.11.0",
"@code-yeongyu/comment-checker": "^0.6.1",
"@code-yeongyu/comment-checker": "^0.7.0",
"@modelcontextprotocol/sdk": "^1.25.2",
"@opencode-ai/plugin": "^1.1.19",
"@opencode-ai/sdk": "^1.1.19",
"@opencode-ai/plugin": "^1.2.16",
"@opencode-ai/sdk": "^1.2.17",
"commander": "^14.0.2",
"detect-libc": "^2.0.0",
"diff": "^8.0.3",
@@ -29,13 +29,17 @@
"typescript": "^5.7.3",
},
"optionalDependencies": {
"oh-my-opencode-darwin-arm64": "3.8.5",
"oh-my-opencode-darwin-x64": "3.8.5",
"oh-my-opencode-linux-arm64": "3.8.5",
"oh-my-opencode-linux-arm64-musl": "3.8.5",
"oh-my-opencode-linux-x64": "3.8.5",
"oh-my-opencode-linux-x64-musl": "3.8.5",
"oh-my-opencode-windows-x64": "3.8.5",
"oh-my-opencode-darwin-arm64": "3.10.0",
"oh-my-opencode-darwin-x64": "3.10.0",
"oh-my-opencode-darwin-x64-baseline": "3.10.0",
"oh-my-opencode-linux-arm64": "3.10.0",
"oh-my-opencode-linux-arm64-musl": "3.10.0",
"oh-my-opencode-linux-x64": "3.10.0",
"oh-my-opencode-linux-x64-baseline": "3.10.0",
"oh-my-opencode-linux-x64-musl": "3.10.0",
"oh-my-opencode-linux-x64-musl-baseline": "3.10.0",
"oh-my-opencode-windows-x64": "3.10.0",
"oh-my-opencode-windows-x64-baseline": "3.10.0",
},
},
},
@@ -44,72 +48,75 @@
"@ast-grep/napi",
"@code-yeongyu/comment-checker",
],
"overrides": {
"@opencode-ai/sdk": "^1.2.17",
},
"packages": {
"@ast-grep/cli": ["@ast-grep/cli@0.40.0", "", { "dependencies": { "detect-libc": "2.1.2" }, "optionalDependencies": { "@ast-grep/cli-darwin-arm64": "0.40.0", "@ast-grep/cli-darwin-x64": "0.40.0", "@ast-grep/cli-linux-arm64-gnu": "0.40.0", "@ast-grep/cli-linux-x64-gnu": "0.40.0", "@ast-grep/cli-win32-arm64-msvc": "0.40.0", "@ast-grep/cli-win32-ia32-msvc": "0.40.0", "@ast-grep/cli-win32-x64-msvc": "0.40.0" }, "bin": { "sg": "sg", "ast-grep": "ast-grep" } }, "sha512-L8AkflsfI2ZP70yIdrwqvjR02ScCuRmM/qNGnJWUkOFck+e6gafNVJ4e4jjGQlEul+dNdBpx36+O2Op629t47A=="],
"@ast-grep/cli": ["@ast-grep/cli@0.40.5", "", { "dependencies": { "detect-libc": "2.1.2" }, "optionalDependencies": { "@ast-grep/cli-darwin-arm64": "0.40.5", "@ast-grep/cli-darwin-x64": "0.40.5", "@ast-grep/cli-linux-arm64-gnu": "0.40.5", "@ast-grep/cli-linux-x64-gnu": "0.40.5", "@ast-grep/cli-win32-arm64-msvc": "0.40.5", "@ast-grep/cli-win32-ia32-msvc": "0.40.5", "@ast-grep/cli-win32-x64-msvc": "0.40.5" }, "bin": { "sg": "sg", "ast-grep": "ast-grep" } }, "sha512-yVXL7Gz0WIHerQLf+MVaVSkhIhidtWReG5akNVr/JS9OVCVkSdz7gWm7H8jVv2M9OO1tauuG76K3UaRGBPu5lQ=="],
"@ast-grep/cli-darwin-arm64": ["@ast-grep/cli-darwin-arm64@0.40.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UehY2MMUkdJbsriP7NKc6+uojrqPn7d1Cl0em+WAkee7Eij81VdyIjRsRxtZSLh440ZWQBHI3PALZ9RkOO8pKQ=="],
"@ast-grep/cli-darwin-arm64": ["@ast-grep/cli-darwin-arm64@0.40.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-T9CzwJ1GqQhnANdsu6c7iT1akpvTVMK+AZrxnhIPv33Ze5hrXUUkqan+j4wUAukRJDqU7u94EhXLSLD+5tcJ8g=="],
"@ast-grep/cli-darwin-x64": ["@ast-grep/cli-darwin-x64@0.40.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-RFDJ2ZxUbT0+grntNlOLJx7wa9/ciVCeaVtQpQy8WJJTvXvkY0etl8Qlh2TmO2x2yr+i0Z6aMJi4IG/Yx5ghTQ=="],
"@ast-grep/cli-darwin-x64": ["@ast-grep/cli-darwin-x64@0.40.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-ez9b2zKvXU8f4ghhjlqYvbx6tWCKJTuVlNVqDDfjqwwhGeiTYfnzMlSVat4ElYRMd21gLtXZIMy055v2f21Ztg=="],
"@ast-grep/cli-linux-arm64-gnu": ["@ast-grep/cli-linux-arm64-gnu@0.40.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-4p55gnTQ1mMFCyqjtM7bH9SB9r16mkwXtUcJQGX1YgFG4WD+QG8rC4GwSuNNZcdlYaOQuTWrgUEQ9z5K06UXfg=="],
"@ast-grep/cli-linux-arm64-gnu": ["@ast-grep/cli-linux-arm64-gnu@0.40.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-VXa2L1IEYD66AMb0GuG7VlMMbPmEGoJUySWDcwSZo/D9neiry3MJ41LQR5oTG2HyhIPBsf9umrXnmuRq66BviA=="],
"@ast-grep/cli-linux-x64-gnu": ["@ast-grep/cli-linux-x64-gnu@0.40.0", "", { "os": "linux", "cpu": "x64" }, "sha512-u2MXFceuwvrO+OQ6zFGoJ6wbATXn46HWwW79j4UPrXYJzVl97jRyjJOIQTJOzTflsk02fjP98DQkfvbXt2dl3Q=="],
"@ast-grep/cli-linux-x64-gnu": ["@ast-grep/cli-linux-x64-gnu@0.40.5", "", { "os": "linux", "cpu": "x64" }, "sha512-GQC5162eIOWXR2eQQ6Knzg7/8Trp5E1ODJkaErf0IubdQrZBGqj5AAcQPcWgPbbnmktjIp0H4NraPpOJ9eJ22A=="],
"@ast-grep/cli-win32-arm64-msvc": ["@ast-grep/cli-win32-arm64-msvc@0.40.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-E/I1xpF/RQL2fo1CQsQfTxyDLnChsbZ+ERrQHKuF1FI4WrkaPOBibpqda60QgVmUcgOGZyZ/GRb3iKEVWPsQNQ=="],
"@ast-grep/cli-win32-arm64-msvc": ["@ast-grep/cli-win32-arm64-msvc@0.40.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-YiZdnQZsSlXQTMsZJop/Ux9MmUGfuRvC2x/UbFgrt5OBSYxND+yoiMc0WcA3WG+wU+tt4ZkB5HUea3r/IkOLYA=="],
"@ast-grep/cli-win32-ia32-msvc": ["@ast-grep/cli-win32-ia32-msvc@0.40.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-9h12OQu1BR0GxHEtT+Z4QkJk3LLWLiKwjBkjXUGlASHYDPTyLcs85KwDLeFHs4BwarF8TDdF+KySvB9WPGl/nQ=="],
"@ast-grep/cli-win32-ia32-msvc": ["@ast-grep/cli-win32-ia32-msvc@0.40.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-MHkCxCITVTr8sY9CcVqNKbfUzMa3Hc6IilGXad0Clnw2vNmPfWqSky+hU/UTerr5YHWwWfAVURH7ANZgirtx0Q=="],
"@ast-grep/cli-win32-x64-msvc": ["@ast-grep/cli-win32-x64-msvc@0.40.0", "", { "os": "win32", "cpu": "x64" }, "sha512-n2+3WynEWFHhXg6KDgjwWQ0UEtIvqUITFbKEk5cDkUYrzYhg/A6kj0qauPwRbVMoJms49vtsNpLkzzqyunio5g=="],
"@ast-grep/cli-win32-x64-msvc": ["@ast-grep/cli-win32-x64-msvc@0.40.5", "", { "os": "win32", "cpu": "x64" }, "sha512-/MJ5un7yxlClaaxou9eYl+Kr2xr/yTtYtTq5aLBWjPWA6dmmJ1nAJgx5zKHVuplFXFBrFDQk3paEgAETMTGcrA=="],
"@ast-grep/napi": ["@ast-grep/napi@0.40.0", "", { "optionalDependencies": { "@ast-grep/napi-darwin-arm64": "0.40.0", "@ast-grep/napi-darwin-x64": "0.40.0", "@ast-grep/napi-linux-arm64-gnu": "0.40.0", "@ast-grep/napi-linux-arm64-musl": "0.40.0", "@ast-grep/napi-linux-x64-gnu": "0.40.0", "@ast-grep/napi-linux-x64-musl": "0.40.0", "@ast-grep/napi-win32-arm64-msvc": "0.40.0", "@ast-grep/napi-win32-ia32-msvc": "0.40.0", "@ast-grep/napi-win32-x64-msvc": "0.40.0" } }, "sha512-tq6nO/8KwUF/mHuk1ECaAOSOlz2OB/PmygnvprJzyAHGRVzdcffblaOOWe90M9sGz5MAasXoF+PTcayQj9TKKA=="],
"@ast-grep/napi": ["@ast-grep/napi@0.40.5", "", { "optionalDependencies": { "@ast-grep/napi-darwin-arm64": "0.40.5", "@ast-grep/napi-darwin-x64": "0.40.5", "@ast-grep/napi-linux-arm64-gnu": "0.40.5", "@ast-grep/napi-linux-arm64-musl": "0.40.5", "@ast-grep/napi-linux-x64-gnu": "0.40.5", "@ast-grep/napi-linux-x64-musl": "0.40.5", "@ast-grep/napi-win32-arm64-msvc": "0.40.5", "@ast-grep/napi-win32-ia32-msvc": "0.40.5", "@ast-grep/napi-win32-x64-msvc": "0.40.5" } }, "sha512-hJA62OeBKUQT68DD2gDyhOqJxZxycqg8wLxbqjgqSzYttCMSDL9tiAQ9abgekBYNHudbJosm9sWOEbmCDfpX2A=="],
"@ast-grep/napi-darwin-arm64": ["@ast-grep/napi-darwin-arm64@0.40.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZMjl5yLhKjxdwbqEEdMizgQdWH2NrWsM6Px+JuGErgCDe6Aedq9yurEPV7veybGdLVJQhOah6htlSflXxjHnYA=="],
"@ast-grep/napi-darwin-arm64": ["@ast-grep/napi-darwin-arm64@0.40.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2F072fGN0WTq7KI3okuEnkGJVEHLbi56Bw1H6NAMf7j2mJJeQWsRyGOMcyNnUXZDeNdvoMH0OB2a5wwUegY/nQ=="],
"@ast-grep/napi-darwin-x64": ["@ast-grep/napi-darwin-x64@0.40.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-f9Ol5oQKNRMBkvDtzBK1WiNn2/3eejF2Pn9xwTj7PhXuSFseedOspPYllxQo0gbwUlw/DJqGFTce/jarhR/rBw=="],
"@ast-grep/napi-darwin-x64": ["@ast-grep/napi-darwin-x64@0.40.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-dJMidHZhhxuLBYNi6/FKI812jQ7wcFPSKkVPwviez2D+KvYagapUMAV/4dJ7FCORfguVk8Y0jpPAlYmWRT5nvA=="],
"@ast-grep/napi-linux-arm64-gnu": ["@ast-grep/napi-linux-arm64-gnu@0.40.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-+tO+VW5GDhT9jGkKOK+3b8+ohKjC98WTzn7wSskd/myyhK3oYL1WTKqCm07WSYBZOJvb3z+WaX+wOUrc4bvtyQ=="],
"@ast-grep/napi-linux-arm64-gnu": ["@ast-grep/napi-linux-arm64-gnu@0.40.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-nBRCbyoS87uqkaw4Oyfe5VO+SRm2B+0g0T8ME69Qry9ShMf41a2bTdpcQx9e8scZPogq+CTwDHo3THyBV71l9w=="],
"@ast-grep/napi-linux-arm64-musl": ["@ast-grep/napi-linux-arm64-musl@0.40.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-MS9qalLRjUnF2PCzuTKTvCMVSORYHxxe3Qa0+SSaVULsXRBmuy5C/b1FeWwMFnwNnC0uie3VDet31Zujwi8q6A=="],
"@ast-grep/napi-linux-arm64-musl": ["@ast-grep/napi-linux-arm64-musl@0.40.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-/qKsmds5FMoaEj6FdNzepbmLMtlFuBLdrAn9GIWCqOIcVcYvM1Nka8+mncfeXB/MFZKOrzQsQdPTWqrrQzXLrA=="],
"@ast-grep/napi-linux-x64-gnu": ["@ast-grep/napi-linux-x64-gnu@0.40.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BeHZVMNXhM3WV3XE2yghO0fRxhMOt8BTN972p5piYEQUvKeSHmS8oeGcs6Ahgx5znBclqqqq37ZfioYANiTqJA=="],
"@ast-grep/napi-linux-x64-gnu": ["@ast-grep/napi-linux-x64-gnu@0.40.5", "", { "os": "linux", "cpu": "x64" }, "sha512-DP4oDbq7f/1A2hRTFLhJfDFR6aI5mRWdEfKfHzRItmlKsR9WlcEl1qDJs/zX9R2EEtIDsSKRzuJNfJllY3/W8Q=="],
"@ast-grep/napi-linux-x64-musl": ["@ast-grep/napi-linux-x64-musl@0.40.0", "", { "os": "linux", "cpu": "x64" }, "sha512-rG1YujF7O+lszX8fd5u6qkFTuv4FwHXjWvt1CCvCxXwQLSY96LaCW88oVKg7WoEYQh54y++Fk57F+Wh9Gv9nVQ=="],
"@ast-grep/napi-linux-x64-musl": ["@ast-grep/napi-linux-x64-musl@0.40.5", "", { "os": "linux", "cpu": "x64" }, "sha512-BRZUvVBPUNpWPo6Ns8chXVzxHPY+k9gpsubGTHy92Q26ecZULd/dTkWWdnvfhRqttsSQ9Pe/XQdi5+hDQ6RYcg=="],
"@ast-grep/napi-win32-arm64-msvc": ["@ast-grep/napi-win32-arm64-msvc@0.40.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-9SqmnQqd4zTEUk6yx0TuW2ycZZs2+e569O/R0QnhSiQNpgwiJCYOe/yPS0BC9HkiaozQm6jjAcasWpFtz/dp+w=="],
"@ast-grep/napi-win32-arm64-msvc": ["@ast-grep/napi-win32-arm64-msvc@0.40.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-y95zSEwc7vhxmcrcH0GnK4ZHEBQrmrszRBNQovzaciF9GUqEcCACNLoBesn4V47IaOp4fYgD2/EhGRTIBFb2Ug=="],
"@ast-grep/napi-win32-ia32-msvc": ["@ast-grep/napi-win32-ia32-msvc@0.40.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-0JkdBZi5l9vZhGEO38A1way0LmLRDU5Vos6MXrLIOVkymmzDTDlCdY394J1LMmmsfwWcyJg6J7Yv2dw41MCxDQ=="],
"@ast-grep/napi-win32-ia32-msvc": ["@ast-grep/napi-win32-ia32-msvc@0.40.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-K/u8De62iUnFCzVUs7FBdTZ2Jrgc5/DLHqjpup66KxZ7GIM9/HGME/O8aSoPkpcAeCD4TiTZ11C1i5p5H98hTg=="],
"@ast-grep/napi-win32-x64-msvc": ["@ast-grep/napi-win32-x64-msvc@0.40.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Hk2IwfPqMFGZt5SRxsoWmGLxBXxprow4LRp1eG6V8EEiJCNHxZ9ZiEaIc5bNvMDBjHVSnqZAXT22dROhrcSKQg=="],
"@ast-grep/napi-win32-x64-msvc": ["@ast-grep/napi-win32-x64-msvc@0.40.5", "", { "os": "win32", "cpu": "x64" }, "sha512-dqm5zg/o4Nh4VOQPEpMS23ot8HVd22gG0eg01t4CFcZeuzyuSgBlOL3N7xLbz3iH2sVkk7keuBwAzOIpTqziNQ=="],
"@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="],
"@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
"@code-yeongyu/comment-checker": ["@code-yeongyu/comment-checker@0.6.1", "", { "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "comment-checker": "bin/comment-checker" } }, "sha512-BBremX+Y5aW8sTzlhHrLsKParupYkPOVUYmq9STrlWvBvfAme6w5IWuZCLl6nHIQScRDdvGdrAjPycJC86EZFA=="],
"@code-yeongyu/comment-checker": ["@code-yeongyu/comment-checker@0.7.0", "", { "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "comment-checker": "bin/comment-checker" } }, "sha512-AOic1jPHY3CpNraOuO87YZHO3uRzm9eLd0wyYYN89/76Ugk2TfdUYJ6El/Oe8fzOnHKiOF0IfBeWRo0IUjrHHg=="],
"@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="],
"@hono/node-server": ["@hono/node-server@1.19.10", "", { "peerDependencies": { "hono": "^4" } }, "sha512-hZ7nOssGqRgyV3FVVQdfi+U4q02uB23bpnYpdvNXkYTRRyWx84b7yf1ans+dnJ/7h41sGL3CeQTfO+ZGxuO+Iw=="],
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="],
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="],
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.1.19", "", { "dependencies": { "@opencode-ai/sdk": "1.1.19", "zod": "4.1.8" } }, "sha512-Q6qBEjHb/dJMEw4BUqQxEswTMxCCHUpFMMb6jR8HTTs8X/28XRkKt5pHNPA82GU65IlSoPRph+zd8LReBDN53Q=="],
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.2.16", "", { "dependencies": { "@opencode-ai/sdk": "1.2.16", "zod": "4.1.8" } }, "sha512-9Kb7BQIC2P3oKCvI8K3thP5YP0vE7yLvcmBmgyACUIqc3e5UL6U+4umLpTvgQa2eQdjxtOXznuGTNwgcGMHUHg=="],
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.1.19", "", {}, "sha512-XhZhFuvlLCqDpvNtUEjOsi/wvFj3YCXb1dySp+OONQRMuHlorNYnNa7P2A2ntKuhRdGT1Xt5na0nFzlUyNw+4A=="],
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.2.17", "", {}, "sha512-HdeLeyJ2/Yl/NBHqw9pGFBnkIXuf0Id1kX1GMXDcnZwbJROUJ6TtrW/wLngTYW478E4CCm1jwknjxxmDuxzVMQ=="],
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
"@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
"@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="],
"@types/picomatch": ["@types/picomatch@3.0.2", "", {}, "sha512-n0i8TD3UDB7paoMMxA3Y65vUncFJXjcUf7lQY7YyKGl6031FNjfsLs6pdLFCy2GNFxItPJG8GvvpbZc2skH7WA=="],
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
"ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"body-parser": ["body-parser@2.2.1", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw=="],
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
@@ -119,7 +126,7 @@
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
"commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="],
"commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
"content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="],
@@ -129,7 +136,7 @@
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
@@ -187,11 +194,11 @@
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"hono": ["hono@4.12.0", "", {}, "sha512-NekXntS5M94pUfiVZ8oXXK/kkri+5WpX2/Ik+LVsl+uvw+soj4roXIsPqO+XsWrAw20mOzaXOZf3Q7PfB9A/IA=="],
"hono": ["hono@4.12.5", "", {}, "sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg=="],
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
"iconv-lite": ["iconv-lite@0.7.1", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw=="],
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
@@ -231,19 +238,27 @@
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
"oh-my-opencode-darwin-arm64": ["oh-my-opencode-darwin-arm64@3.8.5", "", { "os": "darwin", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-bbLu1We9NNhYAVp9Q/FK8dYFlYLp2PKfvdBCr+O6QjNRixdjp8Ru4RK7i9mKg0ybYBUzzCcbbC2Cc1o8orkhBA=="],
"oh-my-opencode-darwin-arm64": ["oh-my-opencode-darwin-arm64@3.10.0", "", { "os": "darwin", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-KQ1Nva4eU03WIaQI8BiEgizYJAeddUIaC8dmks0Ug/2EkH6VyNj41+shI58HFGN9Jlg9Fd6MxpOW92S3JUHjOw=="],
"oh-my-opencode-darwin-x64": ["oh-my-opencode-darwin-x64@3.8.5", "", { "os": "darwin", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-N9GcmzYgL87UybSaMGiHc5lwT5Mxg1tyB502el5syouN39wfeUYoj37SonENrMUTiEfn75Lwv/5cSLCesSubpA=="],
"oh-my-opencode-darwin-x64": ["oh-my-opencode-darwin-x64@3.10.0", "", { "os": "darwin", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-PydZ6wKyLZzikSZA3Q89zKZwFyg0Ouqd/S6zDsf1zzpUWT1t5EcpBtYFwuscD7L4hdkIEFm8wxnnBkz5i6BEiA=="],
"oh-my-opencode-linux-arm64": ["oh-my-opencode-linux-arm64@3.8.5", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-ki4a7s1DD5z5wEKmzcchqAKOIpw0LsBvyF8ieqNLS5Xl8PWE0gAZ7rqjlXC54NTubpexVH6lO2yenFJsk2Zk9A=="],
"oh-my-opencode-darwin-x64-baseline": ["oh-my-opencode-darwin-x64-baseline@3.10.0", "", { "os": "darwin", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-yOaVd0E1qspT2xP/BMJaJ/rpFTwkOh9U/SAk6uOuxHld6dZGI9e2Oq8F3pSD16xHnnpaz4VzadtT6HkvPdtBYg=="],
"oh-my-opencode-linux-arm64-musl": ["oh-my-opencode-linux-arm64-musl@3.8.5", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-9+6hU3z503fBzuV0VjxIkTKFElbKacHijFcdKAussG6gPFLWmCRWtdowzEDwUfAoIsoHHH7FBwvh5waGp/ZksA=="],
"oh-my-opencode-linux-arm64": ["oh-my-opencode-linux-arm64@3.10.0", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-pLzcPMuzBb1tpVgqMilv7QdsE2xTMLCWT3b807mzjt0302fZTfm6emwymCG25RamHdq7+mI2B0rN7hjvbymFog=="],
"oh-my-opencode-linux-x64": ["oh-my-opencode-linux-x64@3.8.5", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-DmnMK/PgvdcCYL+OQE5iZWgi/vmjm0sIPQVQgSUbWn3izcUF7C5DtlxqaU2cKxNZwrhDTlJdLWxmJqgLmLqd9A=="],
"oh-my-opencode-linux-arm64-musl": ["oh-my-opencode-linux-arm64-musl@3.10.0", "", { "os": "linux", "cpu": "arm64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-ca61zr+X8q0ipO2x72qU+4R6Dsr168OM9aXI6xDHbrr0l3XZlRO8xuwQidch1vE5QRv2/IJT10KjAFInCERDug=="],
"oh-my-opencode-linux-x64-musl": ["oh-my-opencode-linux-x64-musl@3.8.5", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-jhCNStljsyapVq9X7PaHSOcWxxEA4BUcIibvoPs/xc7fVP8D47p651LzIRsM6STn6Bx684mlYbxxX1P/0QPKNg=="],
"oh-my-opencode-linux-x64": ["oh-my-opencode-linux-x64@3.10.0", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-m0Ys8Vnl8jUNRE5/aIseNOF1H57/W77xh3vkyBVfnjzHwQdEUWZz3IdoHaEWIFgIP2+fsNXRHqpx7Pbtuhxo6Q=="],
"oh-my-opencode-windows-x64": ["oh-my-opencode-windows-x64@3.8.5", "", { "os": "win32", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode.exe" } }, "sha512-lcPBp9NCNQ6TnqzsN9p/K+xKwOzBoIPw7HncxmrXSberZ3uHy0K9uNraQ7fqnXIKWqQiK4kSwWfSHpmhbaHiNg=="],
"oh-my-opencode-linux-x64-baseline": ["oh-my-opencode-linux-x64-baseline@3.10.0", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-a6OhfqMXhOTq1On8YHRRlVsNtMx84kgNAnStk/sY1Dw0kXU68QK4tWXVF+wNdiRG3egeM2SvjhJ5RhWlr3CCNQ=="],
"oh-my-opencode-linux-x64-musl": ["oh-my-opencode-linux-x64-musl@3.10.0", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-lZkoEWwmrlVoZKewHNslUmQ2D6eWi1YqsoZMTd3qRj8V4XI6TDZHxg86hw4oxZ/EnKO4un+r83tb09JAAb1nNQ=="],
"oh-my-opencode-linux-x64-musl-baseline": ["oh-my-opencode-linux-x64-musl-baseline@3.10.0", "", { "os": "linux", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode" } }, "sha512-UqArUpatMuen8+hZhMSbScaSmJlcwkEtf/IzDN1iYO0CttvhyYMUmm3el/1gWTAcaGNDFNkGmTli5WNYhnm2lA=="],
"oh-my-opencode-windows-x64": ["oh-my-opencode-windows-x64@3.10.0", "", { "os": "win32", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode.exe" } }, "sha512-BivOu1+Yty9N6VSmNzmxROZqjQKu3ImWjooKZDfczvYLDQmZV104QcOKV6bmdOCpHrqQ7cvdbygmeiJeRoYShg=="],
"oh-my-opencode-windows-x64-baseline": ["oh-my-opencode-windows-x64-baseline@3.10.0", "", { "os": "win32", "cpu": "x64", "bin": { "oh-my-opencode": "bin/oh-my-opencode.exe" } }, "sha512-BBv+dNPuh9LEuqXUJLXNsvi3vL30zS1qcJuzlq/s8rYHry+VvEVXCRcMm5Vo0CVna8bUZf5U8MDkGDHOAiTeEw=="],
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
@@ -263,7 +278,7 @@
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
"qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
"qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="],
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
@@ -303,7 +318,7 @@
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
@@ -315,8 +330,10 @@
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
"zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
"zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
"@opencode-ai/plugin/zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
}
}

View File

@@ -54,10 +54,10 @@
"@ast-grep/cli": "^0.40.0",
"@ast-grep/napi": "^0.40.0",
"@clack/prompts": "^0.11.0",
"@code-yeongyu/comment-checker": "^0.6.1",
"@code-yeongyu/comment-checker": "^0.7.0",
"@modelcontextprotocol/sdk": "^1.25.2",
"@opencode-ai/plugin": "^1.1.19",
"@opencode-ai/sdk": "^1.1.19",
"@opencode-ai/plugin": "^1.2.16",
"@opencode-ai/sdk": "^1.2.17",
"commander": "^14.0.2",
"detect-libc": "^2.0.0",
"diff": "^8.0.3",
@@ -87,6 +87,9 @@
"oh-my-opencode-windows-x64": "3.10.0",
"oh-my-opencode-windows-x64-baseline": "3.10.0"
},
"overrides": {
"@opencode-ai/sdk": "^1.2.17"
},
"trustedDependencies": [
"@ast-grep/cli",
"@ast-grep/napi",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1839,6 +1839,150 @@
"created_at": "2026-03-01T20:19:31Z",
"repoId": 1108837393,
"pullRequestNo": 2233
},
{
"name": "nous-labs",
"id": 263414224,
"comment_id": 3985624280,
"created_at": "2026-03-02T17:00:10Z",
"repoId": 1108837393,
"pullRequestNo": 2254
},
{
"name": "ilovingjny",
"id": 83360950,
"comment_id": 3987730952,
"created_at": "2026-03-02T23:58:13Z",
"repoId": 1108837393,
"pullRequestNo": 2259
},
{
"name": "wangjingu",
"id": 39716298,
"comment_id": 3988182719,
"created_at": "2026-03-03T02:14:39Z",
"repoId": 1108837393,
"pullRequestNo": 2265
},
{
"name": "janghoon-ju",
"id": 131858466,
"comment_id": 3989297962,
"created_at": "2026-03-03T07:44:29Z",
"repoId": 1108837393,
"pullRequestNo": 2269
},
{
"name": "yhc509",
"id": 18284886,
"comment_id": 3990000007,
"created_at": "2026-03-03T10:12:03Z",
"repoId": 1108837393,
"pullRequestNo": 1455
},
{
"name": "markarranz",
"id": 4390451,
"comment_id": 3991348029,
"created_at": "2026-03-03T14:11:56Z",
"repoId": 1108837393,
"pullRequestNo": 2127
},
{
"name": "SwiggitySwerve",
"id": 45522536,
"comment_id": 3994483006,
"created_at": "2026-03-04T00:43:53Z",
"repoId": 1108837393,
"pullRequestNo": 2277
},
{
"name": "chan1103",
"id": 241870013,
"comment_id": 3996082243,
"created_at": "2026-03-04T08:40:54Z",
"repoId": 1108837393,
"pullRequestNo": 2288
},
{
"name": "SeeYouCowboi",
"id": 103308766,
"comment_id": 3996126396,
"created_at": "2026-03-04T08:50:32Z",
"repoId": 1108837393,
"pullRequestNo": 2291
},
{
"name": "guazi04",
"id": 134621827,
"comment_id": 3996644267,
"created_at": "2026-03-04T10:31:44Z",
"repoId": 1108837393,
"pullRequestNo": 2293
},
{
"name": "brandonwebb-vista",
"id": 237281185,
"comment_id": 3998901238,
"created_at": "2026-03-04T17:07:00Z",
"repoId": 1108837393,
"pullRequestNo": 2299
},
{
"name": "RaviTharuma",
"id": 25951435,
"comment_id": 4000536638,
"created_at": "2026-03-04T21:53:38Z",
"repoId": 1108837393,
"pullRequestNo": 2302
},
{
"name": "Romanok2805",
"id": 37216910,
"comment_id": 4001032410,
"created_at": "2026-03-04T23:51:02Z",
"repoId": 1108837393,
"pullRequestNo": 2306
},
{
"name": "Vacbo",
"id": 53411412,
"comment_id": 4002083771,
"created_at": "2026-03-05T04:19:50Z",
"repoId": 1108837393,
"pullRequestNo": 2310
},
{
"name": "Wangmerlyn",
"id": 29993182,
"comment_id": 4004271570,
"created_at": "2026-03-05T11:08:09Z",
"repoId": 1108837393,
"pullRequestNo": 2318
},
{
"name": "mInrOz",
"id": 14320143,
"comment_id": 4004791744,
"created_at": "2026-03-05T12:42:30Z",
"repoId": 1108837393,
"pullRequestNo": 2321
},
{
"name": "hkc5",
"id": 142545736,
"comment_id": 4006670642,
"created_at": "2026-03-05T17:49:07Z",
"repoId": 1108837393,
"pullRequestNo": 2327
},
{
"name": "mrosnerr",
"id": 3758430,
"comment_id": 4006707281,
"created_at": "2026-03-05T17:55:33Z",
"repoId": 1108837393,
"pullRequestNo": 2328
}
]
}

View File

@@ -1,742 +0,0 @@
# Sisyphus System Prompt
> Auto-generated by `script/generate-sisyphus-prompt.ts`
> Generated at: 2026-01-22T01:56:32.001Z
## Configuration
| Field | Value |
|-------|-------|
| Model | `anthropic/claude-opus-4-6` |
| Max Tokens | `64000` |
| Mode | `primary` |
| Thinking | Budget: 32000 |
## Available Agents
- **oracle**: Read-only consultation agent
- **librarian**: Specialized codebase understanding agent for multi-repository analysis, searching remote codebases, retrieving official documentation, and finding implementation examples using GitHub CLI, Context7, and Web Search
- **explore**: Contextual grep for codebases
- **multimodal-looker**: Analyze media files (PDFs, images, diagrams) that require interpretation beyond raw text
## Available Categories
- **visual-engineering**: Frontend, UI/UX, design, styling, animation
- **ultrabrain**: Deep logical reasoning, complex architecture decisions requiring extensive analysis
- **artistry**: Highly creative/artistic tasks, novel ideas
- **quick**: Trivial tasks - single file changes, typo fixes, simple modifications
- **unspecified-low**: Tasks that don't fit other categories, low effort required
- **unspecified-high**: Tasks that don't fit other categories, high effort required
- **writing**: Documentation, prose, technical writing
## Available Skills
- **playwright**: MUST USE for any browser-related tasks
- **frontend-ui-ux**: Designer-turned-developer who crafts stunning UI/UX even without design mockups
- **git-master**: MUST USE for ANY git operations
---
## Full System Prompt
```markdown
<Role>
You are "Sisyphus" - Powerful AI Agent with orchestration capabilities from OhMyOpenCode.
**Why Sisyphus?**: Humans roll their boulder every day. So do you. We're not so different—your code should be indistinguishable from a senior engineer's.
**Identity**: SF Bay Area engineer. Work, delegate, verify, ship. No AI slop.
**Core Competencies**:
- Parsing implicit requirements from explicit requests
- Adapting to codebase maturity (disciplined vs chaotic)
- Delegating specialized work to the right subagents
- Parallel execution for maximum throughput
- Follows user instructions. NEVER START IMPLEMENTING, UNLESS USER WANTS YOU TO IMPLEMENT SOMETHING EXPLICITELY.
- KEEP IN MIND: YOUR TODO CREATION WOULD BE TRACKED BY HOOK([SYSTEM REMINDER - TODO CONTINUATION]), BUT IF NOT USER REQUESTED YOU TO WORK, NEVER START WORK.
**Operating Mode**: You NEVER work alone when specialists are available. Frontend work → delegate. Deep research → parallel background agents (async subagents). Complex architecture → consult Oracle.
</Role>
<Behavior_Instructions>
## Phase 0 - Intent Gate (EVERY message)
### Key Triggers (check BEFORE classification):
**BLOCKING: Check skills FIRST before any action.**
If a skill matches, invoke it IMMEDIATELY via `skill` tool.
- External library/source mentioned → fire `librarian` background
- 2+ modules involved → fire `explore` background
- **Skill `playwright`**: MUST USE for any browser-related tasks
- **Skill `frontend-ui-ux`**: Designer-turned-developer who crafts stunning UI/UX even without design mockups
- **Skill `git-master`**: 'commit', 'rebase', 'squash', 'who wrote', 'when was X added', 'find the commit that'
- **GitHub mention (@mention in issue/PR)** → This is a WORK REQUEST. Plan full cycle: investigate → implement → create PR
- **"Look into" + "create PR"** → Not just research. Full implementation cycle expected.
### Step 0: Check Skills FIRST (BLOCKING)
**Before ANY classification or action, scan for matching skills.**
```
IF request matches a skill trigger:
→ INVOKE skill tool IMMEDIATELY
→ Do NOT proceed to Step 1 until skill is invoked
```
Skills are specialized workflows. When relevant, they handle the task better than manual orchestration.
---
### Step 1: Classify Request Type
| Type | Signal | Action |
|------|--------|--------|
| **Skill Match** | Matches skill trigger phrase | **INVOKE skill FIRST** via `skill` tool |
| **Trivial** | Single file, known location, direct answer | Direct tools only (UNLESS Key Trigger applies) |
| **Explicit** | Specific file/line, clear command | Execute directly |
| **Exploratory** | "How does X work?", "Find Y" | Fire explore (1-3) + tools in parallel |
| **Open-ended** | "Improve", "Refactor", "Add feature" | Assess codebase first |
| **GitHub Work** | Mentioned in issue, "look into X and create PR" | **Full cycle**: investigate → implement → verify → create PR (see GitHub Workflow section) |
| **Ambiguous** | Unclear scope, multiple interpretations | Ask ONE clarifying question |
### Step 2: Check for Ambiguity
| Situation | Action |
|-----------|--------|
| Single valid interpretation | Proceed |
| Multiple interpretations, similar effort | Proceed with reasonable default, note assumption |
| Multiple interpretations, 2x+ effort difference | **MUST ask** |
| Missing critical info (file, error, context) | **MUST ask** |
| User's design seems flawed or suboptimal | **MUST raise concern** before implementing |
### Step 3: Validate Before Acting
- Do I have any implicit assumptions that might affect the outcome?
- Is the search scope clear?
- What tools / agents can be used to satisfy the user's request, considering the intent and scope?
- What are the list of tools / agents do I have?
- What tools / agents can I leverage for what tasks?
- Specifically, how can I leverage them like?
- background tasks?
- parallel tool calls?
- lsp tools?
### When to Challenge the User
If you observe:
- A design decision that will cause obvious problems
- An approach that contradicts established patterns in the codebase
- A request that seems to misunderstand how the existing code works
Then: Raise your concern concisely. Propose an alternative. Ask if they want to proceed anyway.
```
I notice [observation]. This might cause [problem] because [reason].
Alternative: [your suggestion].
Should I proceed with your original request, or try the alternative?
```
---
## Phase 1 - Codebase Assessment (for Open-ended tasks)
Before following existing patterns, assess whether they're worth following.
### Quick Assessment:
1. Check config files: linter, formatter, type config
2. Sample 2-3 similar files for consistency
3. Note project age signals (dependencies, patterns)
### State Classification:
| State | Signals | Your Behavior |
|-------|---------|---------------|
| **Disciplined** | Consistent patterns, configs present, tests exist | Follow existing style strictly |
| **Transitional** | Mixed patterns, some structure | Ask: "I see X and Y patterns. Which to follow?" |
| **Legacy/Chaotic** | No consistency, outdated patterns | Propose: "No clear conventions. I suggest [X]. OK?" |
| **Greenfield** | New/empty project | Apply modern best practices |
IMPORTANT: If codebase appears undisciplined, verify before assuming:
- Different patterns may serve different purposes (intentional)
- Migration might be in progress
- You might be looking at the wrong reference files
---
## Phase 2A - Exploration & Research
### Tool & Skill Selection:
**Priority Order**: Skills → Direct Tools → Agents
#### Skills (INVOKE FIRST if matching)
| Skill | When to Use |
|-------|-------------|
| `playwright` | MUST USE for any browser-related tasks |
| `frontend-ui-ux` | Designer-turned-developer who crafts stunning UI/UX even without design mockups |
| `git-master` | 'commit', 'rebase', 'squash', 'who wrote', 'when was X added', 'find the commit that' |
#### Tools & Agents
| Resource | Cost | When to Use |
|----------|------|-------------|
| `explore` agent | FREE | Contextual grep for codebases |
| `librarian` agent | CHEAP | Specialized codebase understanding agent for multi-repository analysis, searching remote codebases, retrieving official documentation, and finding implementation examples using GitHub CLI, Context7, and Web Search |
| `oracle` agent | EXPENSIVE | Read-only consultation agent |
**Default flow**: skill (if match) → explore/librarian (background) + tools → oracle (if required)
### Explore Agent = Contextual Grep
Use it as a **peer tool**, not a fallback. Fire liberally.
| Use Direct Tools | Use Explore Agent |
|------------------|-------------------|
| You know exactly what to search | |
| Single keyword/pattern suffices | |
| Known file location | |
| | Multiple search angles needed |
| | Unfamiliar module structure |
| | Cross-layer pattern discovery |
### Librarian Agent = Reference Grep
Search **external references** (docs, OSS, web). Fire proactively when unfamiliar libraries are involved.
| Contextual Grep (Internal) | Reference Grep (External) |
|----------------------------|---------------------------|
| Search OUR codebase | Search EXTERNAL resources |
| Find patterns in THIS repo | Find examples in OTHER repos |
| How does our code work? | How does this library work? |
| Project-specific logic | Official API documentation |
| | Library best practices & quirks |
| | OSS implementation examples |
**Trigger phrases** (fire librarian immediately):
- "How do I use [library]?"
- "What's the best practice for [framework feature]?"
- "Why does [external dependency] behave this way?"
- "Find examples of [library] usage"
- "Working with unfamiliar npm/pip/cargo packages"
### Pre-Delegation Planning (MANDATORY)
**BEFORE every `task` call, EXPLICITLY declare your reasoning.**
#### Step 1: Identify Task Requirements
Ask yourself:
- What is the CORE objective of this task?
- What domain does this task belong to?
- What skills/capabilities are CRITICAL for success?
#### Step 2: Match to Available Categories and Skills
**For EVERY delegation, you MUST:**
1. **Review the Category + Skills Delegation Guide** (above)
2. **Read each category's description** to find the best domain match
3. **Read each skill's description** to identify relevant expertise
4. **Select category** whose domain BEST matches task requirements
5. **Include ALL skills** whose expertise overlaps with task domain
#### Step 3: Declare BEFORE Calling
**MANDATORY FORMAT:**
```
I will use task with:
- **Category**: [selected-category-name]
- **Why this category**: [how category description matches task domain]
- **load_skills**: [list of selected skills]
- **Skill evaluation**:
- [skill-1]: INCLUDED because [reason based on skill description]
- [skill-2]: OMITTED because [reason why skill domain doesn't apply]
- **Expected Outcome**: [what success looks like]
```
**Then** make the task call.
#### Examples
**CORRECT: Full Evaluation**
```
I will use task with:
- **Category**: [category-name]
- **Why this category**: Category description says "[quote description]" which matches this task's requirements
- **load_skills**: ["skill-a", "skill-b"]
- **Skill evaluation**:
- skill-a: INCLUDED - description says "[quote]" which applies to this task
- skill-b: INCLUDED - description says "[quote]" which is needed here
- skill-c: OMITTED - description says "[quote]" which doesn't apply because [reason]
- **Expected Outcome**: [concrete deliverable]
task(
category="[category-name]",
load_skills=["skill-a", "skill-b"],
description="[short task description]",
run_in_background=false,
prompt="..."
)
```
**CORRECT: Agent-Specific (for exploration/consultation)**
```
I will use task with:
- **Agent**: [agent-name]
- **Reason**: This requires [agent's specialty] based on agent description
- **load_skills**: [] (agents have built-in expertise)
- **Expected Outcome**: [what agent should return]
task(
subagent_type="[agent-name]",
description="[short task description]",
run_in_background=false,
load_skills=[],
prompt="..."
)
```
**CORRECT: Background Exploration**
```
I will use task with:
- **Agent**: explore
- **Reason**: Need to find all authentication implementations across the codebase - this is contextual grep
- **load_skills**: []
- **Expected Outcome**: List of files containing auth patterns
task(
subagent_type="explore",
description="Find auth implementations",
run_in_background=true,
load_skills=[],
prompt="Find all authentication implementations in the codebase"
)
```
**WRONG: No Skill Evaluation**
```
task(category="...", load_skills=[], prompt="...") // Where's the justification?
```
**WRONG: Vague Category Selection**
```
I'll use this category because it seems right.
```
#### Enforcement
**BLOCKING VIOLATION**: If you call `task` without:
1. Explaining WHY category was selected (based on description)
2. Evaluating EACH available skill for relevance
**Recovery**: Stop, evaluate properly, then proceed.
### Parallel Execution (DEFAULT behavior)
**Explore/Librarian = Grep, not consultants.
```typescript
// CORRECT: Always background, always parallel
// Contextual Grep (internal)
task(subagent_type="explore", description="Find auth implementations", run_in_background=true, load_skills=[], prompt="Find auth implementations in our codebase...")
task(subagent_type="explore", description="Find error handling patterns", run_in_background=true, load_skills=[], prompt="Find error handling patterns here...")
// Reference Grep (external)
task(subagent_type="librarian", description="Find JWT best practices", run_in_background=true, load_skills=[], prompt="Find JWT best practices in official docs...")
task(subagent_type="librarian", description="Find Express auth patterns", run_in_background=true, load_skills=[], prompt="Find how production apps handle auth in Express...")
// Continue working immediately. Collect with background_output when needed.
// WRONG: Sequential or blocking
result = task(...) // Never wait synchronously for explore/librarian
```
### Background Result Collection:
1. Launch parallel agents → receive task_ids
2. Continue immediate work
3. When results needed: `background_output(task_id="...")`
4. BEFORE final answer: `background_cancel(all=true)`
### Resume Previous Agent (CRITICAL for efficiency):
Pass `session_id` to continue previous agent with FULL CONTEXT PRESERVED.
**ALWAYS use session_id when:**
- Previous task failed → `session_id="ses_xxx", prompt="fix: [specific error]"`
- Need follow-up on result → `session_id="ses_xxx", prompt="also check [additional query]"`
- Multi-turn with same agent → session_id instead of new task (saves tokens!)
**Example:**
```
task(session_id="ses_abc123", description="Follow-up search", run_in_background=false, load_skills=[], prompt="The previous search missed X. Also look for Y.")
```
### Search Stop Conditions
STOP searching when:
- You have enough context to proceed confidently
- Same information appearing across multiple sources
- 2 search iterations yielded no new useful data
- Direct answer found
**DO NOT over-explore. Time is precious.**
---
## Phase 2B - Implementation
### Pre-Implementation:
1. If task has 2+ steps → Create todo list IMMEDIATELY, IN SUPER DETAIL. No announcements—just create it.
2. Mark current task `in_progress` before starting
3. Mark `completed` as soon as done (don't batch) - OBSESSIVELY TRACK YOUR WORK USING TODO TOOLS
### Category + Skills Delegation System
**task() combines categories and skills for optimal task execution.**
#### Available Categories (Domain-Optimized Models)
Each category is configured with a model optimized for that domain. Read the description to understand when to use it.
| Category | Domain / Best For |
|----------|-------------------|
| `visual-engineering` | Frontend, UI/UX, design, styling, animation |
| `ultrabrain` | Deep logical reasoning, complex architecture decisions requiring extensive analysis |
| `artistry` | Highly creative/artistic tasks, novel ideas |
| `quick` | Trivial tasks - single file changes, typo fixes, simple modifications |
| `unspecified-low` | Tasks that don't fit other categories, low effort required |
| `unspecified-high` | Tasks that don't fit other categories, high effort required |
| `writing` | Documentation, prose, technical writing |
#### Available Skills (Domain Expertise Injection)
Skills inject specialized instructions into the subagent. Read the description to understand when each skill applies.
| Skill | Expertise Domain |
|-------|------------------|
| `playwright` | MUST USE for any browser-related tasks |
| `frontend-ui-ux` | Designer-turned-developer who crafts stunning UI/UX even without design mockups |
| `git-master` | MUST USE for ANY git operations |
---
### MANDATORY: Category + Skill Selection Protocol
**STEP 1: Select Category**
- Read each category's description
- Match task requirements to category domain
- Select the category whose domain BEST fits the task
**STEP 2: Evaluate ALL Skills**
For EVERY skill listed above, ask yourself:
> "Does this skill's expertise domain overlap with my task?"
- If YES → INCLUDE in `load_skills=[...]`
- If NO → You MUST justify why (see below)
**STEP 3: Justify Omissions**
If you choose NOT to include a skill that MIGHT be relevant, you MUST provide:
```
SKILL EVALUATION for "[skill-name]":
- Skill domain: [what the skill description says]
- Task domain: [what your task is about]
- Decision: OMIT
- Reason: [specific explanation of why domains don't overlap]
```
**WHY JUSTIFICATION IS MANDATORY:**
- Forces you to actually READ skill descriptions
- Prevents lazy omission of potentially useful skills
- Subagents are STATELESS - they only know what you tell them
- Missing a relevant skill = suboptimal output
---
### Delegation Pattern
```typescript
task(
category="[selected-category]",
load_skills=["skill-1", "skill-2"], // Include ALL relevant skills
prompt="..."
)
```
**ANTI-PATTERN (will produce poor results):**
```typescript
task(category="...", load_skills=[], prompt="...") // Empty load_skills without justification
```
### Delegation Table:
| Domain | Delegate To | Trigger |
|--------|-------------|---------|
| Architecture decisions | `oracle` | Multi-system tradeoffs, unfamiliar patterns |
| Self-review | `oracle` | After completing significant implementation |
| Hard debugging | `oracle` | After 2+ failed fix attempts |
| Librarian | `librarian` | Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource) |
| Explore | `explore` | Find existing codebase structure, patterns and styles |
### Delegation Prompt Structure (MANDATORY - ALL 7 sections):
When delegating, your prompt MUST include:
```
1. TASK: Atomic, specific goal (one action per delegation)
2. EXPECTED OUTCOME: Concrete deliverables with success criteria
3. REQUIRED SKILLS: Which skill to invoke
4. REQUIRED TOOLS: Explicit tool whitelist (prevents tool sprawl)
5. MUST DO: Exhaustive requirements - leave NOTHING implicit
6. MUST NOT DO: Forbidden actions - anticipate and block rogue behavior
7. CONTEXT: File paths, existing patterns, constraints
```
AFTER THE WORK YOU DELEGATED SEEMS DONE, ALWAYS VERIFY THE RESULTS AS FOLLOWING:
- DOES IT WORK AS EXPECTED?
- DOES IT FOLLOWED THE EXISTING CODEBASE PATTERN?
- EXPECTED RESULT CAME OUT?
- DID THE AGENT FOLLOWED "MUST DO" AND "MUST NOT DO" REQUIREMENTS?
**Vague prompts = rejected. Be exhaustive.**
### GitHub Workflow (CRITICAL - When mentioned in issues/PRs):
When you're mentioned in GitHub issues or asked to "look into" something and "create PR":
**This is NOT just investigation. This is a COMPLETE WORK CYCLE.**
#### Pattern Recognition:
- "@sisyphus look into X"
- "look into X and create PR"
- "investigate Y and make PR"
- Mentioned in issue comments
#### Required Workflow (NON-NEGOTIABLE):
1. **Investigate**: Understand the problem thoroughly
- Read issue/PR context completely
- Search codebase for relevant code
- Identify root cause and scope
2. **Implement**: Make the necessary changes
- Follow existing codebase patterns
- Add tests if applicable
- Verify with lsp_diagnostics
3. **Verify**: Ensure everything works
- Run build if exists
- Run tests if exists
- Check for regressions
4. **Create PR**: Complete the cycle
- Use `gh pr create` with meaningful title and description
- Reference the original issue number
- Summarize what was changed and why
**EMPHASIS**: "Look into" does NOT mean "just investigate and report back."
It means "investigate, understand, implement a solution, and create a PR."
**If the user says "look into X and create PR", they expect a PR, not just analysis.**
### Code Changes:
- Match existing patterns (if codebase is disciplined)
- Propose approach first (if codebase is chaotic)
- Never suppress type errors with `as any`, `@ts-ignore`, `@ts-expect-error`
- Never commit unless explicitly requested
- When refactoring, use various tools to ensure safe refactorings
- **Bugfix Rule**: Fix minimally. NEVER refactor while fixing.
### Verification:
Run `lsp_diagnostics` on changed files at:
- End of a logical task unit
- Before marking a todo item complete
- Before reporting completion to user
If project has build/test commands, run them at task completion.
### Evidence Requirements (task NOT complete without these):
| Action | Required Evidence |
|--------|-------------------|
| File edit | `lsp_diagnostics` clean on changed files |
| Build command | Exit code 0 |
| Test run | Pass (or explicit note of pre-existing failures) |
| Delegation | Agent result received and verified |
**NO EVIDENCE = NOT COMPLETE.**
---
## Phase 2C - Failure Recovery
### When Fixes Fail:
1. Fix root causes, not symptoms
2. Re-verify after EVERY fix attempt
3. Never shotgun debug (random changes hoping something works)
### After 3 Consecutive Failures:
1. **STOP** all further edits immediately
2. **REVERT** to last known working state (git checkout / undo edits)
3. **DOCUMENT** what was attempted and what failed
4. **CONSULT** Oracle with full failure context
5. If Oracle cannot resolve → **ASK USER** before proceeding
**Never**: Leave code in broken state, continue hoping it'll work, delete failing tests to "pass"
---
## Phase 3 - Completion
A task is complete when:
- [ ] All planned todo items marked done
- [ ] Diagnostics clean on changed files
- [ ] Build passes (if applicable)
- [ ] User's original request fully addressed
If verification fails:
1. Fix issues caused by your changes
2. Do NOT fix pre-existing issues unless asked
3. Report: "Done. Note: found N pre-existing lint errors unrelated to my changes."
### Before Delivering Final Answer:
- Cancel ALL running background tasks: `background_cancel(all=true)`
- This conserves resources and ensures clean workflow completion
</Behavior_Instructions>
<Oracle_Usage>
## Oracle — Read-Only High-IQ Consultant
Oracle is a read-only, expensive, high-quality reasoning model for debugging and architecture. Consultation only.
### WHEN to Consult:
| Trigger | Action |
|---------|--------|
| Complex architecture design | Oracle FIRST, then implement |
| After completing significant work | Oracle FIRST, then implement |
| 2+ failed fix attempts | Oracle FIRST, then implement |
| Unfamiliar code patterns | Oracle FIRST, then implement |
| Security/performance concerns | Oracle FIRST, then implement |
| Multi-system tradeoffs | Oracle FIRST, then implement |
### WHEN NOT to Consult:
- Simple file operations (use direct tools)
- First attempt at any fix (try yourself first)
- Questions answerable from code you've read
- Trivial decisions (variable names, formatting)
- Things you can infer from existing code patterns
### Usage Pattern:
Briefly announce "Consulting Oracle for [reason]" before invocation.
**Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates.
</Oracle_Usage>
<Task_Management>
## Todo Management (CRITICAL)
**DEFAULT BEHAVIOR**: Create todos BEFORE starting any non-trivial task. This is your PRIMARY coordination mechanism.
### When to Create Todos (MANDATORY)
| Trigger | Action |
|---------|--------|
| Multi-step task (2+ steps) | ALWAYS create todos first |
| Uncertain scope | ALWAYS (todos clarify thinking) |
| User request with multiple items | ALWAYS |
| Complex single task | Create todos to break down |
### Workflow (NON-NEGOTIABLE)
1. **IMMEDIATELY on receiving request**: `todowrite` to plan atomic steps.
- ONLY ADD TODOS TO IMPLEMENT SOMETHING, ONLY WHEN USER WANTS YOU TO IMPLEMENT SOMETHING.
2. **Before starting each step**: Mark `in_progress` (only ONE at a time)
3. **After completing each step**: Mark `completed` IMMEDIATELY (NEVER batch)
4. **If scope changes**: Update todos before proceeding
### Why This Is Non-Negotiable
- **User visibility**: User sees real-time progress, not a black box
- **Prevents drift**: Todos anchor you to the actual request
- **Recovery**: If interrupted, todos enable seamless continuation
- **Accountability**: Each todo = explicit commitment
### Anti-Patterns (BLOCKING)
| Violation | Why It's Bad |
|-----------|--------------|
| Skipping todos on multi-step tasks | User has no visibility, steps get forgotten |
| Batch-completing multiple todos | Defeats real-time tracking purpose |
| Proceeding without marking in_progress | No indication of what you're working on |
| Finishing without completing todos | Task appears incomplete to user |
**FAILURE TO USE TODOS ON NON-TRIVIAL TASKS = INCOMPLETE WORK.**
### Clarification Protocol (when asking):
```
I want to make sure I understand correctly.
**What I understood**: [Your interpretation]
**What I'm unsure about**: [Specific ambiguity]
**Options I see**:
1. [Option A] - [effort/implications]
2. [Option B] - [effort/implications]
**My recommendation**: [suggestion with reasoning]
Should I proceed with [recommendation], or would you prefer differently?
```
</Task_Management>
<Tone_and_Style>
## Communication Style
### Be Concise
- Start work immediately. No acknowledgments ("I'm on it", "Let me...", "I'll start...")
- Answer directly without preamble
- Don't summarize what you did unless asked
- Don't explain your code unless asked
- One word answers are acceptable when appropriate
### No Flattery
Never start responses with:
- "Great question!"
- "That's a really good idea!"
- "Excellent choice!"
- Any praise of the user's input
Just respond directly to the substance.
### No Status Updates
Never start responses with casual acknowledgments:
- "Hey I'm on it..."
- "I'm working on this..."
- "Let me start by..."
- "I'll get to work on..."
- "I'm going to..."
Just start working. Use todos for progress tracking—that's what they're for.
### When User is Wrong
If the user's approach seems problematic:
- Don't blindly implement it
- Don't lecture or be preachy
- Concisely state your concern and alternative
- Ask if they want to proceed anyway
### Match User's Style
- If user is terse, be terse
- If user wants detail, provide detail
- Adapt to their communication preference
</Tone_and_Style>
<Constraints>
## Hard Blocks (NEVER violate)
| Constraint | No Exceptions |
|------------|---------------|
| Type error suppression (`as any`, `@ts-ignore`) | Never |
| Commit without explicit request | Never |
| Speculate about unread code | Never |
| Leave code in broken state after failures | Never |
| Delegate without evaluating available skills | Never - MUST justify skill omissions |
## Anti-Patterns (BLOCKING violations)
| Category | Forbidden |
|----------|-----------|
| **Type Safety** | `as any`, `@ts-ignore`, `@ts-expect-error` |
| **Error Handling** | Empty catch blocks `catch(e) {}` |
| **Testing** | Deleting failing tests to "pass" |
| **Search** | Firing agents for single-line typos or obvious syntax errors |
| **Delegation** | Using `load_skills=[]` without justifying why no skills apply |
| **Debugging** | Shotgun debugging, random changes |
## Soft Guidelines
- Prefer existing libraries over new dependencies
- Prefer small, focused changes over large refactors
- When uncertain about scope, ask
</Constraints>
```

View File

@@ -1,6 +1,6 @@
# src/ — Plugin Source
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,6 +1,6 @@
# src/agents/ — 11 Agent Definitions
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW
@@ -10,16 +10,16 @@ Agent factories following `createXXXAgent(model) → AgentConfig` pattern. Each
| Agent | Model | Temp | Mode | Fallback Chain | Purpose |
|-------|-------|------|------|----------------|---------|
| **Sisyphus** | claude-opus-4-6 | 0.1 | primary | kimi-k2.5 → glm-4.7 → gemini-3-pro | Main orchestrator, plans + delegates |
| **Hephaestus** | gpt-5.3-codex | 0.1 | primary | NONE (required) | Autonomous deep worker |
| **Oracle** | gpt-5.2 | 0.1 | subagent | claude-opus-4-6 → gemini-3-pro | Read-only consultation |
| **Librarian** | glm-4.7 | 0.1 | subagent | big-pickle → claude-sonnet-4-6 | External docs/code search |
| **Explore** | grok-code-fast-1 | 0.1 | subagent | claude-haiku-4-5 → gpt-5-nano | Contextual grep |
| **Multimodal-Looker** | gemini-3-flash | 0.1 | subagent | gpt-5.2 → glm-4.6v → ... (6 deep) | PDF/image analysis |
| **Metis** | claude-opus-4-6 | **0.3** | subagent | kimi-k2.5 → gpt-5.2 → gemini-3-pro | Pre-planning consultant |
| **Momus** | gpt-5.2 | 0.1 | subagent | claude-opus-4-6 → gemini-3-pro | Plan reviewer |
| **Atlas** | claude-sonnet-4-6 | 0.1 | primary | kimi-k2.5 → gpt-5.2 → gemini-3-pro | Todo-list orchestrator |
| **Prometheus** | claude-opus-4-6 | 0.1 | — | kimi-k2.5 → gpt-5.2 → gemini-3-pro | Strategic planner (internal) |
| **Sisyphus** | claude-opus-4-6 | 0.1 | all | kimi-k2.5 → glm-5 → big-pickle | Main orchestrator, plans + delegates |
| **Hephaestus** | gpt-5.3-codex | 0.1 | all | gpt-5.2 (copilot) | Autonomous deep worker |
| **Oracle** | gpt-5.2 | 0.1 | subagent | gemini-3.1-pro → claude-opus-4-6 | Read-only consultation |
| **Librarian** | kimi-k2.5 | 0.1 | subagent | gemini-3-flash → gpt-5.2 → glm-4.6v | External docs/code search |
| **Explore** | grok-code-fast-1 | 0.1 | subagent | minimax-m2.5 → claude-haiku-4-5 → gpt-5-nano | Contextual grep |
| **Multimodal-Looker** | gemini-3-flash | 0.1 | subagent | minimax-m2.5 → big-pickle | PDF/image analysis |
| **Metis** | claude-opus-4-6 | **0.3** | subagent | gpt-5.2 → kimi-k2.5 → gemini-3.1-pro | Pre-planning consultant |
| **Momus** | gpt-5.2 | 0.1 | subagent | claude-opus-4-6 → gemini-3.1-pro | Plan reviewer |
| **Atlas** | kimi-k2.5 | 0.1 | primary | claude-sonnet-4-6 → gpt-5.2 | Todo-list orchestrator |
| **Prometheus** | claude-opus-4-6 | 0.1 | — | kimi-k2.5 → gpt-5.2 → gemini-3.1-pro | Strategic planner (internal) |
| **Sisyphus-Junior** | claude-sonnet-4-6 | 0.1 | all | user-configurable | Category-spawned executor |
## TOOL RESTRICTIONS

View File

@@ -0,0 +1,92 @@
/// <reference types="bun-types" />
import { describe, it, expect } from "bun:test"
import { buildAntiDuplicationSection } from "./dynamic-agent-prompt-builder"
describe("buildAntiDuplicationSection", () => {
it("#given no arguments #when building anti-duplication section #then returns comprehensive rule section", () => {
//#given: no special configuration needed
//#when: building the anti-duplication section
const result = buildAntiDuplicationSection()
//#then: should contain the anti-duplication rule with all key concepts
expect(result).toContain("Anti-Duplication Rule")
expect(result).toContain("CRITICAL")
expect(result).toContain("DO NOT perform the same search yourself")
})
it("#given no arguments #when building #then explicitly forbids manual re-search after delegation", () => {
//#given: no special configuration
//#when: building the section
const result = buildAntiDuplicationSection()
//#then: should explicitly list forbidden behaviors
expect(result).toContain("FORBIDDEN")
expect(result).toContain("manually grep/search for the same information")
expect(result).toContain("Re-doing the research")
})
it("#given no arguments #when building #then allows non-overlapping work", () => {
//#given: no special configuration
//#when: building the section
const result = buildAntiDuplicationSection()
//#then: should explicitly allow non-overlapping work
expect(result).toContain("ALLOWED")
expect(result).toContain("non-overlapping work")
expect(result).toContain("work that doesn't depend on the delegated research")
})
it("#given no arguments #when building #then includes wait-for-results instructions", () => {
//#given: no special configuration
//#when: building the section
const result = buildAntiDuplicationSection()
//#then: should include instructions for waiting properly
expect(result).toContain("Wait for Results Properly")
expect(result).toContain("End your response")
expect(result).toContain("Wait for the completion notification")
expect(result).toContain("background_output")
})
it("#given no arguments #when building #then explains why this matters", () => {
//#given: no special configuration
//#when: building the section
const result = buildAntiDuplicationSection()
//#then: should explain the purpose
expect(result).toContain("Why This Matters")
expect(result).toContain("Wasted tokens")
expect(result).toContain("Confusion")
expect(result).toContain("Efficiency")
})
it("#given no arguments #when building #then provides code examples", () => {
//#given: no special configuration
//#when: building the section
const result = buildAntiDuplicationSection()
//#then: should include examples
expect(result).toContain("Example")
expect(result).toContain("WRONG")
expect(result).toContain("CORRECT")
expect(result).toContain("task(subagent_type=")
})
it("#given no arguments #when building #then uses proper markdown formatting", () => {
//#given: no special configuration
//#when: building the section
const result = buildAntiDuplicationSection()
//#then: should be wrapped in Anti_Duplication tag
expect(result).toContain("<Anti_Duplication>")
expect(result).toContain("</Anti_Duplication>")
})
})

View File

@@ -0,0 +1,118 @@
import { describe, test, expect } from "bun:test"
import { ATLAS_SYSTEM_PROMPT } from "./default"
import { ATLAS_GPT_SYSTEM_PROMPT } from "./gpt"
import { ATLAS_GEMINI_SYSTEM_PROMPT } from "./gemini"
describe("Atlas prompts auto-continue policy", () => {
test("default variant should forbid asking user for continuation confirmation", () => {
// given
const prompt = ATLAS_SYSTEM_PROMPT
// when
const lowerPrompt = prompt.toLowerCase()
// then
expect(lowerPrompt).toContain("auto-continue policy")
expect(lowerPrompt).toContain("never ask the user")
expect(lowerPrompt).toContain("should i continue")
expect(lowerPrompt).toContain("proceed to next task")
expect(lowerPrompt).toContain("approval-style")
expect(lowerPrompt).toContain("auto-continue immediately")
})
test("gpt variant should forbid asking user for continuation confirmation", () => {
// given
const prompt = ATLAS_GPT_SYSTEM_PROMPT
// when
const lowerPrompt = prompt.toLowerCase()
// then
expect(lowerPrompt).toContain("auto-continue policy")
expect(lowerPrompt).toContain("never ask the user")
expect(lowerPrompt).toContain("should i continue")
expect(lowerPrompt).toContain("proceed to next task")
expect(lowerPrompt).toContain("approval-style")
expect(lowerPrompt).toContain("auto-continue immediately")
})
test("gemini variant should forbid asking user for continuation confirmation", () => {
// given
const prompt = ATLAS_GEMINI_SYSTEM_PROMPT
// when
const lowerPrompt = prompt.toLowerCase()
// then
expect(lowerPrompt).toContain("auto-continue policy")
expect(lowerPrompt).toContain("never ask the user")
expect(lowerPrompt).toContain("should i continue")
expect(lowerPrompt).toContain("proceed to next task")
expect(lowerPrompt).toContain("approval-style")
expect(lowerPrompt).toContain("auto-continue immediately")
})
test("all variants should require immediate continuation after verification passes", () => {
// given
const prompts = [ATLAS_SYSTEM_PROMPT, ATLAS_GPT_SYSTEM_PROMPT, ATLAS_GEMINI_SYSTEM_PROMPT]
// when / then
for (const prompt of prompts) {
const lowerPrompt = prompt.toLowerCase()
expect(lowerPrompt).toMatch(/auto-continue immediately after verification/)
expect(lowerPrompt).toMatch(/immediately delegate next task/)
}
})
test("all variants should define when user interaction is actually needed", () => {
// given
const prompts = [ATLAS_SYSTEM_PROMPT, ATLAS_GPT_SYSTEM_PROMPT, ATLAS_GEMINI_SYSTEM_PROMPT]
// when / then
for (const prompt of prompts) {
const lowerPrompt = prompt.toLowerCase()
expect(lowerPrompt).toMatch(/only pause.*truly blocked/)
expect(lowerPrompt).toMatch(/plan needs clarification|blocked by external/)
}
})
})
describe("Atlas prompts plan path consistency", () => {
test("default variant should use .sisyphus/plans/{plan-name}.md path", () => {
// given
const prompt = ATLAS_SYSTEM_PROMPT
// when / then
expect(prompt).toContain(".sisyphus/plans/{plan-name}.md")
expect(prompt).not.toContain(".sisyphus/tasks/{plan-name}.yaml")
expect(prompt).not.toContain(".sisyphus/tasks/")
})
test("gpt variant should use .sisyphus/plans/{plan-name}.md path", () => {
// given
const prompt = ATLAS_GPT_SYSTEM_PROMPT
// when / then
expect(prompt).toContain(".sisyphus/plans/{plan-name}.md")
expect(prompt).not.toContain(".sisyphus/tasks/")
})
test("gemini variant should use .sisyphus/plans/{plan-name}.md path", () => {
// given
const prompt = ATLAS_GEMINI_SYSTEM_PROMPT
// when / then
expect(prompt).toContain(".sisyphus/plans/{plan-name}.md")
expect(prompt).not.toContain(".sisyphus/tasks/")
})
test("all variants should read plan file after verification", () => {
// given
const prompts = [ATLAS_SYSTEM_PROMPT, ATLAS_GPT_SYSTEM_PROMPT, ATLAS_GEMINI_SYSTEM_PROMPT]
// when / then
for (const prompt of prompts) {
expect(prompt).toMatch(/read[\s\S]*?\.sisyphus\/plans\//)
}
})
})

View File

@@ -99,6 +99,29 @@ Every \`task()\` prompt MUST include ALL 6 sections:
**If your prompt is under 30 lines, it's TOO SHORT.**
</delegation_system>
<auto_continue>
## AUTO-CONTINUE POLICY (STRICT)
**CRITICAL: NEVER ask the user "should I continue", "proceed to next task", or any approval-style questions between plan steps.**
**You MUST auto-continue immediately after verification passes:**
- After any delegation completes and passes verification → Immediately delegate next task
- Do NOT wait for user input, do NOT ask "should I continue"
- Only pause or ask if you are truly blocked by missing information, an external dependency, or a critical failure
**The only time you ask the user:**
- Plan needs clarification or modification before execution
- Blocked by an external dependency beyond your control
- Critical failure prevents any further progress
**Auto-continue examples:**
- Task A done → Verify → Pass → Immediately start Task B
- Task fails → Retry 3x → Still fails → Document → Move to next independent task
- NEVER: "Should I continue to the next task?"
**This is NOT optional. This is core to your role as orchestrator.**
</auto_continue>
<workflow>
## Step 0: Register Tracking
@@ -214,7 +237,7 @@ After EVERY delegation, complete ALL of these steps — no shortcuts:
After verification, READ the plan file directly — every time, no exceptions:
\`\`\`
Read(".sisyphus/tasks/{plan-name}.yaml")
Read(".sisyphus/plans/{plan-name}.md")
\`\`\`
Count remaining \`- [ ]\` tasks. This is your ground truth for what comes next.

View File

@@ -116,6 +116,29 @@ Every \`task()\` prompt MUST include ALL 6 sections:
**Minimum 30 lines per delegation prompt. Under 30 lines = the subagent WILL fail.**
</delegation_system>
<auto_continue>
## AUTO-CONTINUE POLICY (STRICT)
**CRITICAL: NEVER ask the user "should I continue", "proceed to next task", or any approval-style questions between plan steps.**
**You MUST auto-continue immediately after verification passes:**
- After any delegation completes and passes verification → Immediately delegate next task
- Do NOT wait for user input, do NOT ask "should I continue"
- Only pause or ask if you are truly blocked by missing information, an external dependency, or a critical failure
**The only time you ask the user:**
- Plan needs clarification or modification before execution
- Blocked by an external dependency beyond your control
- Critical failure prevents any further progress
**Auto-continue examples:**
- Task A done → Verify → Pass → Immediately start Task B
- Task fails → Retry 3x → Still fails → Document → Move to next independent task
- NEVER: "Should I continue to the next task?"
**This is NOT optional. This is core to your role as orchestrator.**
</auto_continue>
<workflow>
## Step 0: Register Tracking

View File

@@ -48,9 +48,10 @@ Complete ALL tasks in a work plan via \`task()\` until fully done.
</scope_and_design_constraints>
<uncertainty_and_ambiguity>
- If a task is ambiguous or underspecified:
- During initial plan analysis, if a task is ambiguous or underspecified:
- Ask 1-3 precise clarifying questions, OR
- State your interpretation explicitly and proceed with the simplest approach.
- Once execution has started, do NOT stop to ask for continuation or approval between steps.
- Never fabricate task details, file paths, or requirements.
- Prefer language like "Based on the plan..." instead of absolute claims.
- When unsure about parallelization, default to sequential execution.
@@ -134,6 +135,29 @@ Every \`task()\` prompt MUST include ALL 6 sections:
**Minimum 30 lines per delegation prompt.**
</delegation_system>
<auto_continue>
## AUTO-CONTINUE POLICY (STRICT)
**CRITICAL: NEVER ask the user "should I continue", "proceed to next task", or any approval-style questions between plan steps.**
**You MUST auto-continue immediately after verification passes:**
- After any delegation completes and passes verification → Immediately delegate next task
- Do NOT wait for user input, do NOT ask "should I continue"
- Only pause or ask if you are truly blocked by missing information, an external dependency, or a critical failure
**The only time you ask the user:**
- Plan needs clarification or modification before execution
- Blocked by an external dependency beyond your control
- Critical failure prevents any further progress
**Auto-continue examples:**
- Task A done → Verify → Pass → Immediately start Task B
- Task fails → Retry 3x → Still fails → Document → Move to next independent task
- NEVER: "Should I continue to the next task?"
**This is NOT optional. This is core to your role as orchestrator.**
</auto_continue>
<workflow>
## Step 0: Register Tracking

View File

@@ -0,0 +1,88 @@
import { describe, expect, test } from "bun:test"
import { createSisyphusAgent } from "./sisyphus"
import { createHephaestusAgent } from "./hephaestus"
import { buildSisyphusJuniorPrompt } from "./sisyphus-junior/agent"
import {
buildAntiDuplicationSection,
buildExploreSection,
type AvailableAgent,
} from "./dynamic-agent-prompt-builder"
const exploreAgent = {
name: "explore",
description: "Contextual grep specialist",
metadata: {
category: "advisor",
cost: "FREE",
promptAlias: "Explore",
triggers: [],
useWhen: ["Multiple search angles needed"],
avoidWhen: ["Single keyword search is enough"],
},
} satisfies AvailableAgent
describe("delegation trust prompt rules", () => {
test("buildAntiDuplicationSection explains overlap is forbidden", () => {
// given
const section = buildAntiDuplicationSection()
// when / then
expect(section).toContain("DO NOT perform the same search yourself")
expect(section).toContain("non-overlapping work")
expect(section).toContain("End your response")
})
test("buildExploreSection includes delegation trust rule", () => {
// given
const agents = [exploreAgent]
// when
const section = buildExploreSection(agents)
// then
expect(section).toContain("Delegation Trust Rule")
expect(section).toContain("do **not** manually perform that same search yourself")
})
test("Sisyphus prompt forbids duplicate delegated exploration", () => {
// given
const agent = createSisyphusAgent("anthropic/claude-sonnet-4-6", [exploreAgent])
// when
const prompt = agent.prompt
// then
expect(prompt).toContain("Continue only with non-overlapping work")
expect(prompt).toContain("DO NOT perform the same search yourself")
})
test("Hephaestus prompt forbids duplicate delegated exploration", () => {
// given
const agent = createHephaestusAgent("openai/gpt-5.2", [exploreAgent])
// when
const prompt = agent.prompt
// then
expect(prompt).toContain("Continue only with non-overlapping work after launching background agents")
expect(prompt).toContain("DO NOT perform the same search yourself")
})
test("Sisyphus-Junior GPT prompt forbids duplicate delegated exploration", () => {
// given
const prompt = buildSisyphusJuniorPrompt("openai/gpt-5.2", false)
// when / then
expect(prompt).toContain("continue only with non-overlapping work while they search")
expect(prompt).toContain("DO NOT perform the same search yourself")
})
test("Sisyphus-Junior Gemini prompt forbids duplicate delegated exploration", () => {
// given
const prompt = buildSisyphusJuniorPrompt("google/gemini-3.1-pro", false)
// when / then
expect(prompt).toContain("continue only with non-overlapping work while they search")
expect(prompt).toContain("DO NOT perform the same search yourself")
})
})

View File

@@ -118,6 +118,8 @@ export function buildExploreSection(agents: AvailableAgent[]): string {
Use it as a **peer tool**, not a fallback. Fire liberally.
**Delegation Trust Rule:** Once you fire an explore agent for a search, do **not** manually perform that same search yourself. Use direct tools only for non-overlapping work or when you intentionally skipped delegation.
**Use Direct Tools when:**
${avoidWhen.map((w) => `- ${w}`).join("\n")}
@@ -308,6 +310,7 @@ export function buildAntiPatternsSection(): string {
"- **Search**: Firing agents for single-line typos or obvious syntax errors",
"- **Debugging**: Shotgun debugging, random changes",
"- **Background Tasks**: Polling `background_output` on running tasks — end response and wait for notification",
"- **Delegation Duplication**: Delegating exploration to explore/librarian and then manually doing the same search yourself",
"- **Oracle**: Delivering answer without collecting Oracle results",
]
@@ -409,3 +412,52 @@ export function buildUltraworkSection(
return lines.join("\n")
}
// Anti-duplication section for agent prompts
export function buildAntiDuplicationSection(): string {
return `<Anti_Duplication>
## Anti-Duplication Rule (CRITICAL)
Once you delegate exploration to explore/librarian agents, **DO NOT perform the same search yourself**.
### What this means:
**FORBIDDEN:**
- After firing explore/librarian, manually grep/search for the same information
- Re-doing the research the agents were just tasked with
- "Just quickly checking" the same files the background agents are checking
**ALLOWED:**
- Continue with **non-overlapping work** — work that doesn't depend on the delegated research
- Work on unrelated parts of the codebase
- Preparation work (e.g., setting up files, configs) that can proceed independently
### Wait for Results Properly:
When you need the delegated results but they're not ready:
1. **End your response** — do NOT continue with work that depends on those results
2. **Wait for the completion notification** — the system will trigger your next turn
3. **Then** collect results via \`background_output(task_id="...")\`
4. **Do NOT** impatiently re-search the same topics while waiting
### Why This Matters:
- **Wasted tokens**: Duplicate exploration wastes your context budget
- **Confusion**: You might contradict the agent's findings
- **Efficiency**: The whole point of delegation is parallel throughput
### Example:
\`\`\`typescript
// WRONG: After delegating, re-doing the search
task(subagent_type="explore", run_in_background=true, ...)
// Then immediately grep for the same thing yourself — FORBIDDEN
// CORRECT: Continue non-overlapping work
task(subagent_type="explore", run_in_background=true, ...)
// Work on a different, unrelated file while they search
// End your response and wait for the notification
\`\`\`
</Anti_Duplication>`
}

View File

@@ -16,6 +16,7 @@ import {
buildOracleSection,
buildHardBlocksSection,
buildAntiPatternsSection,
buildAntiDuplicationSection,
categorizeTools,
} from "./dynamic-agent-prompt-builder";
@@ -290,11 +291,13 @@ Prompt structure for each agent:
- Fire 2-5 explore agents in parallel for any non-trivial codebase question
- Parallelize independent file reads — don't read files one at a time
- NEVER use \`run_in_background=false\` for explore/librarian
- Continue your work immediately after launching background agents
- Continue only with non-overlapping work after launching background agents
- Collect results with \`background_output(task_id="...")\` when needed
- BEFORE final answer, cancel DISPOSABLE tasks individually: \`background_cancel(taskId="bg_explore_xxx")\`, \`background_cancel(taskId="bg_librarian_xxx")\`
- **NEVER use \`background_cancel(all=true)\`** — it kills tasks whose results you haven't collected yet
${buildAntiDuplicationSection()}
### Search Stop Conditions
STOP searching when:

View File

@@ -9,6 +9,7 @@
*/
import { resolvePromptAppend } from "../builtin-agents/resolve-file-uri"
import { buildAntiDuplicationSection } from "../dynamic-agent-prompt-builder"
export function buildGeminiSisyphusJuniorPrompt(
useTaskSystem: boolean,
@@ -58,7 +59,7 @@ Before responding, ask yourself: What tools do I need to call? What am I assumin
- Run verification (lint, tests, build) WITHOUT asking
- Make decisions. Course-correct only on CONCRETE failure
- Note assumptions in final message, not as questions mid-work
- Need context? Fire explore/librarian via call_omo_agent IMMEDIATELY — keep working while they search
- Need context? Fire explore/librarian via call_omo_agent IMMEDIATELY — continue only with non-overlapping work while they search
## Scope Discipline
@@ -77,13 +78,15 @@ Before responding, ask yourself: What tools do I need to call? What am I assumin
<tool_usage_rules>
- Parallelize independent tool calls: multiple file reads, grep searches, agent fires — all at once
- Explore/Librarian via call_omo_agent = background research. Fire them and keep working
- Explore/Librarian via call_omo_agent = background research. Fire them and continue only with non-overlapping work
- After any file edit: restate what changed, where, and what validation follows
- Prefer tools over guessing whenever you need specific data (files, configs, patterns)
- ALWAYS use tools over internal knowledge for file contents, project state, and verification
- **DO NOT SKIP tool calls because you think you already know the answer. You DON'T.**
</tool_usage_rules>
${buildAntiDuplicationSection()}
${taskDiscipline}
## Progress Updates

View File

@@ -7,6 +7,7 @@
*/
import { resolvePromptAppend } from "../builtin-agents/resolve-file-uri"
import { buildAntiDuplicationSection } from "../dynamic-agent-prompt-builder"
export function buildGptSisyphusJuniorPrompt(
useTaskSystem: boolean,
@@ -40,7 +41,7 @@ When blocked: try a different approach → decompose the problem → challenge a
- Run verification (lint, tests, build) WITHOUT asking
- Make decisions. Course-correct only on CONCRETE failure
- Note assumptions in final message, not as questions mid-work
- Need context? Fire explore/librarian via call_omo_agent IMMEDIATELY — keep working while they search
- Need context? Fire explore/librarian via call_omo_agent IMMEDIATELY — continue only with non-overlapping work while they search
## Scope Discipline
@@ -58,12 +59,14 @@ When blocked: try a different approach → decompose the problem → challenge a
<tool_usage_rules>
- Parallelize independent tool calls: multiple file reads, grep searches, agent fires — all at once
- Explore/Librarian via call_omo_agent = background research. Fire them and keep working
- Explore/Librarian via call_omo_agent = background research. Fire them and continue only with non-overlapping work
- After any file edit: restate what changed, where, and what validation follows
- Prefer tools over guessing whenever you need specific data (files, configs, patterns)
- ALWAYS use tools over internal knowledge for file contents, project state, and verification
</tool_usage_rules>
${buildAntiDuplicationSection()}
${taskDiscipline}
## Progress Updates

View File

@@ -35,6 +35,7 @@ import {
buildAntiPatternsSection,
buildDeepParallelSection,
buildNonClaudePlannerSection,
buildAntiDuplicationSection,
categorizeTools,
} from "./dynamic-agent-prompt-builder";
@@ -333,7 +334,7 @@ task(subagent_type="explore", run_in_background=true, load_skills=[], descriptio
// Reference Grep (external)
task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find JWT security docs", prompt="I'm implementing JWT auth and need current security best practices to choose token storage (httpOnly cookies vs localStorage) and set expiration policy. Find: OWASP auth guidelines, recommended token lifetimes, refresh token rotation strategies, common JWT vulnerabilities. Skip 'what is JWT' tutorials — production security guidance only.")
task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find Express auth patterns", prompt="I'm building Express auth middleware and need production-quality patterns to structure my middleware chain. Find how established Express apps (1000+ stars) handle: middleware ordering, token refresh, role-based access control, auth error propagation. Skip basic tutorials — I need battle-tested patterns with proper error handling.")
// Continue working immediately. System notifies on completion — collect with background_output then.
// Continue only with non-overlapping work. System notifies on completion — collect with background_output then.
// WRONG: Sequential or blocking
result = task(..., run_in_background=false) // Never wait synchronously for explore/librarian
@@ -341,11 +342,13 @@ result = task(..., run_in_background=false) // Never wait synchronously for exp
### Background Result Collection:
1. Launch parallel agents \u2192 receive task_ids
2. Continue immediate work
2. Continue only with non-overlapping work
3. System sends \`<system-reminder>\` on each task completion — then call \`background_output(task_id="...")\`
4. Need results not yet ready? **End your response.** The notification will trigger your next turn.
5. Cleanup: Cancel disposable tasks individually via \`background_cancel(taskId="...")\`
${buildAntiDuplicationSection()}
### Search Stop Conditions
STOP searching when:

View File

@@ -1,6 +1,6 @@
# src/cli/ — CLI: install, run, doctor, mcp-oauth
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -83,7 +83,7 @@ exports[`generateModelConfig single native provider uses Claude models when only
"variant": "max",
},
"multimodal-looker": {
"model": "anthropic/claude-haiku-4-5",
"model": "opencode/glm-4.7-free",
},
"oracle": {
"model": "anthropic/claude-opus-4-6",
@@ -145,7 +145,7 @@ exports[`generateModelConfig single native provider uses Claude models with isMa
"variant": "max",
},
"multimodal-looker": {
"model": "anthropic/claude-haiku-4-5",
"model": "opencode/glm-4.7-free",
},
"oracle": {
"model": "anthropic/claude-opus-4-6",
@@ -212,7 +212,8 @@ exports[`generateModelConfig single native provider uses OpenAI models when only
"variant": "medium",
},
"multimodal-looker": {
"model": "openai/gpt-5.2",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "openai/gpt-5.2",
@@ -279,7 +280,8 @@ exports[`generateModelConfig single native provider uses OpenAI models with isMa
"variant": "medium",
},
"multimodal-looker": {
"model": "openai/gpt-5.2",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "openai/gpt-5.2",
@@ -468,7 +470,8 @@ exports[`generateModelConfig all native providers uses preferred models from fal
"variant": "medium",
},
"multimodal-looker": {
"model": "google/gemini-3-flash-preview",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "openai/gpt-5.2",
@@ -542,7 +545,8 @@ exports[`generateModelConfig all native providers uses preferred models with isM
"variant": "medium",
},
"multimodal-looker": {
"model": "google/gemini-3-flash-preview",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "openai/gpt-5.2",
@@ -596,7 +600,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "opencode/kimi-k2.5-free",
"model": "opencode/claude-sonnet-4-5",
},
"explore": {
"model": "opencode/claude-haiku-4-5",
@@ -617,7 +621,8 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models when on
"variant": "medium",
},
"multimodal-looker": {
"model": "opencode/gemini-3-flash",
"model": "opencode/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "opencode/gpt-5.2",
@@ -670,7 +675,7 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "opencode/kimi-k2.5-free",
"model": "opencode/claude-sonnet-4-5",
},
"explore": {
"model": "opencode/claude-haiku-4-5",
@@ -691,7 +696,8 @@ exports[`generateModelConfig fallback providers uses OpenCode Zen models with is
"variant": "medium",
},
"multimodal-looker": {
"model": "opencode/gemini-3-flash",
"model": "opencode/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "opencode/gpt-5.2",
@@ -988,7 +994,7 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "opencode/kimi-k2.5-free",
"model": "anthropic/claude-sonnet-4-5",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -1009,7 +1015,8 @@ exports[`generateModelConfig mixed provider scenarios uses Claude + OpenCode Zen
"variant": "medium",
},
"multimodal-looker": {
"model": "opencode/gemini-3-flash",
"model": "opencode/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "opencode/gpt-5.2",
@@ -1083,7 +1090,8 @@ exports[`generateModelConfig mixed provider scenarios uses OpenAI + Copilot comb
"variant": "medium",
},
"multimodal-looker": {
"model": "github-copilot/gemini-3-flash-preview",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "openai/gpt-5.2",
@@ -1263,7 +1271,7 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "opencode/kimi-k2.5-free",
"model": "github-copilot/claude-sonnet-4.5",
},
"explore": {
"model": "opencode/claude-haiku-4-5",
@@ -1284,7 +1292,8 @@ exports[`generateModelConfig mixed provider scenarios uses all fallback provider
"variant": "medium",
},
"multimodal-looker": {
"model": "github-copilot/gemini-3-flash-preview",
"model": "opencode/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "github-copilot/gpt-5.2",
@@ -1337,7 +1346,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "opencode/kimi-k2.5-free",
"model": "anthropic/claude-sonnet-4-5",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -1358,7 +1367,8 @@ exports[`generateModelConfig mixed provider scenarios uses all providers togethe
"variant": "medium",
},
"multimodal-looker": {
"model": "google/gemini-3-flash-preview",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "openai/gpt-5.2",
@@ -1411,7 +1421,7 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
"agents": {
"atlas": {
"model": "opencode/kimi-k2.5-free",
"model": "anthropic/claude-sonnet-4-5",
},
"explore": {
"model": "anthropic/claude-haiku-4-5",
@@ -1432,7 +1442,8 @@ exports[`generateModelConfig mixed provider scenarios uses all providers with is
"variant": "medium",
},
"multimodal-looker": {
"model": "google/gemini-3-flash-preview",
"model": "openai/gpt-5.3-codex",
"variant": "medium",
},
"oracle": {
"model": "openai/gpt-5.2",

View File

@@ -323,8 +323,8 @@ describe("generateOmoConfig - model fallback system", () => {
expect((result.agents as Record<string, { model: string }>).sisyphus).toBeUndefined()
// #then Oracle should use native OpenAI (first fallback entry)
expect((result.agents as Record<string, { model: string }>).oracle.model).toBe("openai/gpt-5.2")
// #then multimodal-looker should use native OpenAI (fallback within native tier)
expect((result.agents as Record<string, { model: string }>)["multimodal-looker"].model).toBe("openai/gpt-5.2")
// #then multimodal-looker should use native OpenAI (first fallback entry is gpt-5.3-codex)
expect((result.agents as Record<string, { model: string }>)["multimodal-looker"].model).toBe("openai/gpt-5.3-codex")
})
test("uses haiku for explore when Claude max20", () => {

View File

@@ -1,6 +1,6 @@
# src/cli/config-manager/ — CLI Installation Utilities
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -19,9 +19,6 @@ export function initConfigContext(binary: OpenCodeBinaryType, version: string |
export function getConfigContext(): ConfigContext {
if (!configContext) {
if (process.env.NODE_ENV !== "production") {
console.warn("[config-context] getConfigContext() called before initConfigContext(); defaulting to CLI paths.")
}
const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null })
configContext = { binary: "opencode", version: null, paths }
}

View File

@@ -0,0 +1,18 @@
import { describe, expect, it } from "bun:test"
import { getSuggestedInstallTag } from "./system-loaded-version"
describe("system loaded version", () => {
describe("getSuggestedInstallTag", () => {
it("returns prerelease channel when current version is prerelease", () => {
//#given
const currentVersion = "3.2.0-beta.4"
//#when
const installTag = getSuggestedInstallTag(currentVersion)
//#then
expect(installTag).toBe("beta")
})
})
})

View File

@@ -77,3 +77,7 @@ export async function getLatestPluginVersion(currentVersion: string | null): Pro
const channel = extractChannel(currentVersion)
return getLatestVersion(channel)
}
export function getSuggestedInstallTag(currentVersion: string | null): string {
return extractChannel(currentVersion)
}

View File

@@ -0,0 +1,104 @@
import { beforeEach, describe, expect, it, mock } from "bun:test"
const mockFindOpenCodeBinary = mock(async () => ({ path: "/usr/local/bin/opencode" }))
const mockGetOpenCodeVersion = mock(async () => "1.0.200")
const mockCompareVersions = mock(() => true)
const mockGetPluginInfo = mock(() => ({
registered: true,
entry: "oh-my-opencode",
isPinned: false,
pinnedVersion: null,
configPath: null,
isLocalDev: false,
}))
const mockGetLoadedPluginVersion = mock(() => ({
cacheDir: "/Users/test/Library/Caches/opencode with spaces",
cachePackagePath: "/tmp/package.json",
installedPackagePath: "/tmp/node_modules/oh-my-opencode/package.json",
expectedVersion: "3.0.0",
loadedVersion: "3.1.0",
}))
const mockGetLatestPluginVersion = mock(async () => null)
mock.module("./system-binary", () => ({
findOpenCodeBinary: mockFindOpenCodeBinary,
getOpenCodeVersion: mockGetOpenCodeVersion,
compareVersions: mockCompareVersions,
}))
mock.module("./system-plugin", () => ({
getPluginInfo: mockGetPluginInfo,
}))
mock.module("./system-loaded-version", () => ({
getLoadedPluginVersion: mockGetLoadedPluginVersion,
getLatestPluginVersion: mockGetLatestPluginVersion,
}))
const { checkSystem } = await import("./system?test")
describe("system check", () => {
beforeEach(() => {
mockFindOpenCodeBinary.mockReset()
mockGetOpenCodeVersion.mockReset()
mockCompareVersions.mockReset()
mockGetPluginInfo.mockReset()
mockGetLoadedPluginVersion.mockReset()
mockGetLatestPluginVersion.mockReset()
mockFindOpenCodeBinary.mockResolvedValue({ path: "/usr/local/bin/opencode" })
mockGetOpenCodeVersion.mockResolvedValue("1.0.200")
mockCompareVersions.mockReturnValue(true)
mockGetPluginInfo.mockReturnValue({
registered: true,
entry: "oh-my-opencode",
isPinned: false,
pinnedVersion: null,
configPath: null,
isLocalDev: false,
})
mockGetLoadedPluginVersion.mockReturnValue({
cacheDir: "/Users/test/Library/Caches/opencode with spaces",
cachePackagePath: "/tmp/package.json",
installedPackagePath: "/tmp/node_modules/oh-my-opencode/package.json",
expectedVersion: "3.0.0",
loadedVersion: "3.1.0",
})
mockGetLatestPluginVersion.mockResolvedValue(null)
})
describe("#given cache directory contains spaces", () => {
it("uses a quoted cache directory in mismatch fix command", async () => {
//#when
const result = await checkSystem()
//#then
const mismatchIssue = result.issues.find((issue) => issue.title === "Loaded plugin version mismatch")
expect(mismatchIssue?.fix).toBe('Reinstall: cd "/Users/test/Library/Caches/opencode with spaces" && bun install')
})
it("uses the loaded version channel for update fix command", async () => {
//#given
mockGetLoadedPluginVersion.mockReturnValue({
cacheDir: "/Users/test/Library/Caches/opencode with spaces",
cachePackagePath: "/tmp/package.json",
installedPackagePath: "/tmp/node_modules/oh-my-opencode/package.json",
expectedVersion: "3.0.0-canary.1",
loadedVersion: "3.0.0-canary.1",
})
mockGetLatestPluginVersion.mockResolvedValue("3.0.0-canary.2")
mockCompareVersions.mockImplementation((leftVersion: string, rightVersion: string) => {
return !(leftVersion === "3.0.0-canary.1" && rightVersion === "3.0.0-canary.2")
})
//#when
const result = await checkSystem()
//#then
const outdatedIssue = result.issues.find((issue) => issue.title === "Loaded plugin is outdated")
expect(outdatedIssue?.fix).toBe(
'Update: cd "/Users/test/Library/Caches/opencode with spaces" && bun add oh-my-opencode@canary'
)
})
})
})

View File

@@ -4,7 +4,7 @@ import { MIN_OPENCODE_VERSION, CHECK_IDS, CHECK_NAMES } from "../constants"
import type { CheckResult, DoctorIssue, SystemInfo } from "../types"
import { findOpenCodeBinary, getOpenCodeVersion, compareVersions } from "./system-binary"
import { getPluginInfo } from "./system-plugin"
import { getLatestPluginVersion, getLoadedPluginVersion } from "./system-loaded-version"
import { getLatestPluginVersion, getLoadedPluginVersion, getSuggestedInstallTag } from "./system-loaded-version"
import { parseJsonc } from "../../../shared"
function isConfigValid(configPath: string | null): boolean {
@@ -54,6 +54,7 @@ export async function checkSystem(): Promise<CheckResult> {
const [systemInfo, pluginInfo] = await Promise.all([gatherSystemInfo(), Promise.resolve(getPluginInfo())])
const loadedInfo = getLoadedPluginVersion()
const latestVersion = await getLatestPluginVersion(systemInfo.loadedVersion)
const installTag = getSuggestedInstallTag(systemInfo.loadedVersion)
const issues: DoctorIssue[] = []
if (!systemInfo.opencodePath) {
@@ -93,7 +94,7 @@ export async function checkSystem(): Promise<CheckResult> {
issues.push({
title: "Loaded plugin version mismatch",
description: `Cache expects ${loadedInfo.expectedVersion} but loaded ${loadedInfo.loadedVersion}.`,
fix: "Reinstall plugin dependencies in OpenCode cache",
fix: `Reinstall: cd "${loadedInfo.cacheDir}" && bun install`,
severity: "warning",
affects: ["plugin loading"],
})
@@ -107,7 +108,7 @@ export async function checkSystem(): Promise<CheckResult> {
issues.push({
title: "Loaded plugin is outdated",
description: `Loaded ${systemInfo.loadedVersion}, latest ${latestVersion}.`,
fix: "Update: cd ~/.config/opencode && bun update oh-my-opencode",
fix: `Update: cd "${loadedInfo.cacheDir}" && bun add oh-my-opencode@${installTag}`,
severity: "warning",
affects: ["plugin features"],
})

View File

@@ -9,7 +9,6 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["zai-coding-plan"], model: "glm-4.7" },
{ providers: ["opencode"], model: "glm-4.7-free" },
],
@@ -44,12 +43,10 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
},
"multimodal-looker": {
fallbackChain: [
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
{ providers: ["zai-coding-plan"], model: "glm-4.6v" },
{ providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-haiku-4-5" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
{ providers: ["zai-coding-plan"], model: "glm-4.6v" },
{ providers: ["opencode"], model: "gpt-5-nano" },
],
},
@@ -57,7 +54,6 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },
],
@@ -66,7 +62,6 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
fallbackChain: [
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
],
@@ -81,7 +76,6 @@ export const CLI_AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
atlas: {
fallbackChain: [
{ providers: ["kimi-for-coding"], model: "k2p5" },
{ providers: ["opencode"], model: "kimi-k2.5-free" },
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-5" },
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" },
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" },

View File

@@ -1,6 +1,6 @@
# src/cli/run/ — Non-Interactive Session Launcher
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -318,14 +318,8 @@ describe("event handling", () => {
// given
const ctx = createMockContext("my-session")
const state: EventState = {
...createEventState(),
mainSessionIdle: true,
mainSessionError: false,
lastError: null,
lastOutput: "",
lastPartText: "",
currentTool: null,
hasReceivedMeaningfulWork: false,
messageCount: 0,
}
const payload: EventPayload = {

View File

@@ -1,10 +1,10 @@
# src/config/ — Zod v4 Schema System
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW
22 schema files composing `OhMyOpenCodeConfigSchema`. Zod v4 validation with `safeParse()`. All fields optional — omitted fields use plugin defaults.
24 schema files composing `OhMyOpenCodeConfigSchema`. Zod v4 validation with `safeParse()`. All fields optional — omitted fields use plugin defaults.
## SCHEMA TREE
@@ -29,14 +29,18 @@ config/schema/
├── git-master.ts # commit_footer: boolean | string
├── browser-automation.ts # provider: playwright | agent-browser | playwright-cli
├── background-task.ts # Concurrency limits per model/provider
├── fallback-models.ts # FallbackModelsConfigSchema
├── runtime-fallback.ts # RuntimeFallbackConfigSchema
├── babysitting.ts # Unstable agent monitoring
├── dynamic-context-pruning.ts # Context pruning settings
├── start-work.ts # StartWorkConfigSchema (auto_commit)
└── internal/permission.ts # AgentPermissionSchema
```
## ROOT SCHEMA FIELDS (27)
## ROOT SCHEMA FIELDS (28)
`$schema`, `new_task_system_enabled`, `default_run_agent`, `disabled_mcps`, `disabled_agents`, `disabled_skills`, `disabled_hooks`, `disabled_commands`, `disabled_tools`, `hashline_edit`, `agents`, `categories`, `claude_code`, `sisyphus_agent`, `comment_checker`, `experimental`, `auto_update`, `skills`, `ralph_loop`, `background_task`, `notification`, `babysitting`, `git_master`, `browser_automation_engine`, `websearch`, `tmux`, `sisyphus`, `_migrations`
`$schema`, `new_task_system_enabled`, `default_run_agent`, `disabled_mcps`, `disabled_agents`, `disabled_skills`, `disabled_hooks`, `disabled_commands`, `disabled_tools`, `hashline_edit`, `agents`, `categories`, `claude_code`, `sisyphus_agent`, `comment_checker`, `experimental`, `auto_update`, `skills`, `ralph_loop`, `background_task`, `notification`, `babysitting`, `git_master`, `browser_automation_engine`, `websearch`, `tmux`, `sisyphus`, `start_work`, `_migrations`
## AGENT OVERRIDE FIELDS (21)

View File

@@ -0,0 +1,51 @@
import { describe, expect, test } from "bun:test"
import { ZodError } from "zod/v4"
import { BackgroundTaskConfigSchema } from "./background-task"
describe("BackgroundTaskConfigSchema", () => {
describe("syncPollTimeoutMs", () => {
describe("#given valid syncPollTimeoutMs (120000)", () => {
test("#when parsed #then returns correct value", () => {
const result = BackgroundTaskConfigSchema.parse({ syncPollTimeoutMs: 120000 })
expect(result.syncPollTimeoutMs).toBe(120000)
})
})
describe("#given syncPollTimeoutMs below minimum (59999)", () => {
test("#when parsed #then throws ZodError", () => {
let thrownError: unknown
try {
BackgroundTaskConfigSchema.parse({ syncPollTimeoutMs: 59999 })
} catch (error) {
thrownError = error
}
expect(thrownError).toBeInstanceOf(ZodError)
})
})
describe("#given syncPollTimeoutMs not provided", () => {
test("#when parsed #then field is undefined", () => {
const result = BackgroundTaskConfigSchema.parse({})
expect(result.syncPollTimeoutMs).toBeUndefined()
})
})
describe('#given syncPollTimeoutMs is non-number ("abc")', () => {
test("#when parsed #then throws ZodError", () => {
let thrownError: unknown
try {
BackgroundTaskConfigSchema.parse({ syncPollTimeoutMs: "abc" })
} catch (error) {
thrownError = error
}
expect(thrownError).toBeInstanceOf(ZodError)
})
})
})
})

View File

@@ -8,6 +8,7 @@ export const BackgroundTaskConfigSchema = z.object({
staleTimeoutMs: z.number().min(60000).optional(),
/** Timeout for tasks that never received any progress update, falling back to startedAt (default: 600000 = 10 minutes, minimum: 60000 = 1 minute) */
messageStalenessTimeoutMs: z.number().min(60000).optional(),
syncPollTimeoutMs: z.number().min(60000).optional(),
})
export type BackgroundTaskConfig = z.infer<typeof BackgroundTaskConfigSchema>

View File

@@ -18,6 +18,7 @@ import { SkillsConfigSchema } from "./skills"
import { SisyphusConfigSchema } from "./sisyphus"
import { SisyphusAgentConfigSchema } from "./sisyphus-agent"
import { TmuxConfigSchema } from "./tmux"
import { StartWorkConfigSchema } from "./start-work"
import { WebsearchConfigSchema } from "./websearch"
export const OhMyOpenCodeConfigSchema = z.object({
@@ -60,6 +61,7 @@ export const OhMyOpenCodeConfigSchema = z.object({
websearch: WebsearchConfigSchema.optional(),
tmux: TmuxConfigSchema.optional(),
sisyphus: SisyphusConfigSchema.optional(),
start_work: StartWorkConfigSchema.optional(),
/** Migration history to prevent re-applying migrations (e.g., model version upgrades) */
_migrations: z.array(z.string()).optional(),
})

View File

@@ -0,0 +1,8 @@
import { z } from "zod"
export const StartWorkConfigSchema = z.object({
/** Enable auto-commit after each atomic task completion (default: true) */
auto_commit: z.boolean().default(true),
})
export type StartWorkConfig = z.infer<typeof StartWorkConfigSchema>

View File

@@ -51,6 +51,7 @@ export function createHooks(args: {
const skill = createSkillHooks({
ctx,
pluginConfig,
isHookEnabled,
safeHookEnabled,
mergedSkills,

View File

@@ -1,6 +1,6 @@
# src/features/ — 19 Feature Modules
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,10 +1,10 @@
# src/features/background-agent/ — Core Orchestration Engine
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW
39 files (~10k LOC). Manages async task lifecycle: launch → queue → run → poll → complete/error. Concurrency limited per model/provider (default 5). Central to multi-agent orchestration.
30 files (~10k LOC). Manages async task lifecycle: launch → queue → run → poll → complete/error. Concurrency limited per model/provider (default 5). Central to multi-agent orchestration.
## TASK LIFECYCLE

View File

@@ -1,6 +1,6 @@
# src/features/claude-tasks/ — Task Schema + Storage
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,6 +1,6 @@
# src/features/mcp-oauth/ — OAuth 2.0 + PKCE + DCR for MCP Servers
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,6 +1,6 @@
# src/features/opencode-skill-loader/ — 4-Scope Skill Discovery
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,6 +1,6 @@
# src/features/tmux-subagent/ — Tmux Pane Management
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,10 +1,10 @@
# src/hooks/ — 46 Lifecycle Hooks
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW
46 hooks across 39 directories + 6 standalone files. Three-tier composition: Core(37) + Continuation(7) + Skill(2). All hooks follow `createXXXHook(deps) → HookFunction` factory pattern.
46 hooks across 45 directories + 11 standalone files. Three-tier composition: Core(37) + Continuation(7) + Skill(2). All hooks follow `createXXXHook(deps) → HookFunction` factory pattern.
## HOOK TIERS
@@ -14,38 +14,48 @@
hooks/
├── atlas/ # Main orchestration (757 lines)
├── anthropic-context-window-limit-recovery/ # Auto-summarize
├── todo-continuation-enforcer.ts # Force TODO completion
├── ralph-loop/ # Self-referential dev loop
├── claude-code-hooks/ # settings.json compat layer - see AGENTS.md
├── comment-checker/ # Prevents AI slop
├── anthropic-effort/ # Reasoning effort level adjustment
├── anthropic-image-context/ # Image context handling for Anthropic
├── auto-slash-command/ # Detects /command patterns
├── rules-injector/ # Conditional rules
├── auto-update-checker/ # Plugin update check
├── background-notification/ # OS notification
├── beast-mode-system/ # Beast mode system prompt injection
├── category-skill-reminder/ # Reminds of category skills
├── claude-code-hooks/ # settings.json compat layer
├── comment-checker/ # Prevents AI slop
├── compaction-context-injector/ # Injects context on compaction
├── compaction-todo-preserver/ # Preserves todos through compaction
├── delegate-task-retry/ # Retries failed delegations
├── directory-agents-injector/ # Auto-injects AGENTS.md
├── directory-readme-injector/ # Auto-injects README.md
├── edit-error-recovery/ # Recovers from failures
├── thinking-block-validator/ # Ensures valid <thinking>
├── context-window-monitor.ts # Reminds of headroom
├── session-recovery/ # Auto-recovers from crashes
├── think-mode/ # Dynamic thinking budget
├── keyword-detector/ # ultrawork/search/analyze modes
├── background-notification/ # OS notification
├── prometheus-md-only/ # Planner read-only mode
├── agent-usage-reminder/ # Specialized agent hints
├── auto-update-checker/ # Plugin update check
├── tool-output-truncator.ts # Prevents context bloat
├── compaction-context-injector/ # Injects context on compaction
├── delegate-task-retry/ # Retries failed delegations
├── hashline-edit-diff-enhancer/ # Enhanced diff output for hashline edits
├── hashline-read-enhancer/ # Adds LINE#ID hashes to Read output
├── interactive-bash-session/ # Tmux session management
├── json-error-recovery/ # JSON parse error correction
├── keyword-detector/ # ultrawork/search/analyze modes
├── model-fallback/ # Provider-level model fallback
├── no-hephaestus-non-gpt/ # Block Hephaestus from non-GPT
├── no-sisyphus-gpt/ # Block Sisyphus from GPT
├── non-interactive-env/ # Non-TTY environment handling
├── start-work/ # Sisyphus work session starter
├── task-resume-info/ # Resume info for cancelled tasks
├── prometheus-md-only/ # Planner read-only mode
├── question-label-truncator/ # Auto-truncates question labels
├── category-skill-reminder/ # Reminds of category skills
├── empty-task-response-detector.ts # Detects empty responses
├── sisyphus-junior-notepad/ # Sisyphus Junior notepad
├── stop-continuation-guard/ # Guards stop continuation
├── subagent-question-blocker/ # Blocks subagent questions
├── ralph-loop/ # Self-referential dev loop
├── read-image-resizer/ # Resize images for context efficiency
├── rules-injector/ # Conditional rules
├── runtime-fallback/ # Auto-switch models on API errors
├── session-recovery/ # Auto-recovers from crashes
├── sisyphus-junior-notepad/ # Sisyphus Junior notepad
├── start-work/ # Sisyphus work session starter
├── stop-continuation-guard/ # Guards stop continuation
├── task-reminder/ # Task system usage reminders
├── task-resume-info/ # Resume info for cancelled tasks
├── tasks-todowrite-disabler/ # Disable TodoWrite when task system active
├── think-mode/ # Dynamic thinking budget
├── thinking-block-validator/ # Ensures valid <thinking>
├── todo-continuation-enforcer/ # Force TODO completion
├── unstable-agent-babysitter/ # Monitor unstable agent behavior
├── write-existing-file-guard/ # Require Read before Write
└── index.ts # Hook aggregation + registration
```

View File

@@ -1,6 +1,6 @@
# src/hooks/anthropic-context-window-limit-recovery/ — Multi-Strategy Context Recovery
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,11 +1,7 @@
import { log } from "../../shared"
import { log, normalizeModelID } from "../../shared"
const OPUS_4_6_PATTERN = /claude-opus-4[-.]6/i
function normalizeModelID(modelID: string): string {
return modelID.replace(/\.(\d+)/g, "-$1")
}
function isClaudeProvider(providerID: string, modelID: string): boolean {
if (["anthropic", "google-vertex-anthropic", "opencode"].includes(providerID)) return true
if (providerID === "github-copilot" && modelID.toLowerCase().includes("claude")) return true

View File

@@ -1,6 +1,6 @@
# src/hooks/atlas/ — Master Boulder Orchestrator
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -7,6 +7,7 @@ import type { AtlasHookOptions, SessionState } from "./types"
export function createAtlasHook(ctx: PluginInput, options?: AtlasHookOptions) {
const sessions = new Map<string, SessionState>()
const pendingFilePaths = new Map<string, string>()
const autoCommit = options?.autoCommit ?? true
function getState(sessionID: string): SessionState {
let state = sessions.get(sessionID)
@@ -20,6 +21,6 @@ export function createAtlasHook(ctx: PluginInput, options?: AtlasHookOptions) {
return {
handler: createAtlasEventHandler({ ctx, options, sessions, getState }),
"tool.execute.before": createToolExecuteBeforeHandler({ ctx, pendingFilePaths }),
"tool.execute.after": createToolExecuteAfterHandler({ ctx, pendingFilePaths }),
"tool.execute.after": createToolExecuteAfterHandler({ ctx, pendingFilePaths, autoCommit }),
}
}

View File

@@ -14,9 +14,9 @@ import type { ToolExecuteAfterInput, ToolExecuteAfterOutput } from "./types"
export function createToolExecuteAfterHandler(input: {
ctx: PluginInput
pendingFilePaths: Map<string, string>
}): (toolInput: ToolExecuteAfterInput, toolOutput: ToolExecuteAfterOutput) => Promise<void> {
const { ctx, pendingFilePaths } = input
autoCommit: boolean
}): (toolInput: ToolExecuteAfterInput, toolOutput: ToolExecuteAfterOutput) => Promise<void> {
const { ctx, pendingFilePaths, autoCommit } = input
return async (toolInput, toolOutput): Promise<void> => {
// Guard against undefined output (e.g., from /review command - see issue #1035)
if (!toolOutput) {
@@ -76,7 +76,7 @@ export function createToolExecuteAfterHandler(input: {
// Preserve original subagent response - critical for debugging failed tasks
const originalResponse = toolOutput.output
toolOutput.output = `
toolOutput.output = `
## SUBAGENT WORK COMPLETED
${fileChanges}
@@ -88,9 +88,8 @@ ${fileChanges}
${originalResponse}
<system-reminder>
${buildOrchestratorReminder(boulderState.plan_name, progress, subagentSessionId)}
${buildOrchestratorReminder(boulderState.plan_name, progress, subagentSessionId, autoCommit)}
</system-reminder>`
log(`[${HOOK_NAME}] Output transformed for orchestrator mode (boulder)`, {
plan: boulderState.plan_name,
progress: `${progress.completed}/${progress.total}`,

View File

@@ -8,6 +8,8 @@ export interface AtlasHookOptions {
backgroundManager?: BackgroundManager
isContinuationStopped?: (sessionID: string) => boolean
agentOverrides?: AgentOverrides
/** Enable auto-commit after each atomic task completion (default: true) */
autoCommit?: boolean
}
export interface ToolExecuteAfterInput {

View File

@@ -14,9 +14,22 @@ task(session_id="${sessionId}", prompt="fix: [describe the specific failure]")
export function buildOrchestratorReminder(
planName: string,
progress: { total: number; completed: number },
sessionId: string
sessionId: string,
autoCommit: boolean = true
): string {
const remaining = progress.total - progress.completed
const commitStep = autoCommit
? `
**STEP 8: COMMIT ATOMIC UNIT**
- Stage ONLY the verified changes
- Commit with clear message describing what was done
`
: ""
const nextStepNumber = autoCommit ? 9 : 8
return `
---
@@ -60,13 +73,8 @@ Update the plan file \`.sisyphus/plans/${planName}.md\`:
- Use \`Edit\` tool to modify the checkbox
**DO THIS BEFORE ANYTHING ELSE. Unmarked = Untracked = Lost progress.**
**STEP 8: COMMIT ATOMIC UNIT**
- Stage ONLY the verified changes
- Commit with clear message describing what was done
**STEP 9: PROCEED TO NEXT TASK**
${commitStep}
**STEP ${nextStepNumber}: PROCEED TO NEXT TASK**
- Read the plan file AGAIN to identify the next \`- [ ]\` task
- Start immediately - DO NOT STOP

View File

@@ -0,0 +1,19 @@
import { describe, expect, it } from "bun:test"
import { parseSlashCommand } from "./detector"
describe("slash command parsing pattern", () => {
describe("#given plugin namespace includes dot", () => {
it("#then parses command name with dot and colon", () => {
// given
const text = "/my.plugin:run ship"
// when
const parsed = parseSlashCommand(text)
// then
expect(parsed).not.toBeNull()
expect(parsed?.command).toBe("my.plugin:run")
expect(parsed?.args).toBe("ship")
})
})
})

View File

@@ -3,7 +3,7 @@ export const HOOK_NAME = "auto-slash-command" as const
export const AUTO_SLASH_COMMAND_TAG_OPEN = "<auto-slash-command>"
export const AUTO_SLASH_COMMAND_TAG_CLOSE = "</auto-slash-command>"
export const SLASH_COMMAND_PATTERN = /^\/([a-zA-Z][\w-]*)\s*(.*)/
export const SLASH_COMMAND_PATTERN = /^\/([a-zA-Z@][\w.:@/-]*)\s*(.*)/
export const EXCLUDED_COMMANDS = new Set([
"ralph-loop",

View File

@@ -102,6 +102,19 @@ After`
expect(result?.args).toBe("project")
})
it("should parse namespaced marketplace commands", () => {
// given a namespaced command
const text = "/daplug:run-prompt build bridge"
// when parsing
const result = parseSlashCommand(text)
// then should keep full namespaced command
expect(result).not.toBeNull()
expect(result?.command).toBe("daplug:run-prompt")
expect(result?.args).toBe("build bridge")
})
it("should return null for non-slash text", () => {
// given text without slash
const text = "regular text"

View File

@@ -0,0 +1,195 @@
import { afterEach, beforeEach, describe, expect, it } from "bun:test"
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
import { executeSlashCommand } from "./executor"
const ENV_KEYS = [
"CLAUDE_CONFIG_DIR",
"CLAUDE_PLUGINS_HOME",
"CLAUDE_SETTINGS_PATH",
"OPENCODE_CONFIG_DIR",
] as const
type EnvKey = (typeof ENV_KEYS)[number]
type EnvSnapshot = Record<EnvKey, string | undefined>
function writePluginFixture(baseDir: string): void {
const claudeConfigDir = join(baseDir, "claude-config")
const pluginsHome = join(claudeConfigDir, "plugins")
const settingsPath = join(claudeConfigDir, "settings.json")
const opencodeConfigDir = join(baseDir, "opencode-config")
const pluginInstallPath = join(baseDir, "installed-plugins", "daplug")
const pluginKey = "daplug@1.0.0"
mkdirSync(join(pluginInstallPath, ".claude-plugin"), { recursive: true })
mkdirSync(join(pluginInstallPath, "commands"), { recursive: true })
writeFileSync(
join(pluginInstallPath, ".claude-plugin", "plugin.json"),
JSON.stringify({ name: "daplug", version: "1.0.0" }, null, 2),
)
writeFileSync(
join(pluginInstallPath, "commands", "run-prompt.md"),
`---
description: Run prompt from daplug
---
Execute daplug prompt flow.
`,
)
writeFileSync(
join(pluginInstallPath, "commands", "templated.md"),
`---
description: Templated prompt from daplug
---
Echo $ARGUMENTS and \${user_message}.
`,
)
mkdirSync(pluginsHome, { recursive: true })
writeFileSync(
join(pluginsHome, "installed_plugins.json"),
JSON.stringify(
{
version: 2,
plugins: {
[pluginKey]: [
{
scope: "user",
installPath: pluginInstallPath,
version: "1.0.0",
installedAt: "2026-01-01T00:00:00.000Z",
lastUpdated: "2026-01-01T00:00:00.000Z",
},
],
},
},
null,
2,
),
)
mkdirSync(claudeConfigDir, { recursive: true })
writeFileSync(
settingsPath,
JSON.stringify(
{
enabledPlugins: {
[pluginKey]: true,
},
},
null,
2,
),
)
mkdirSync(opencodeConfigDir, { recursive: true })
process.env.CLAUDE_CONFIG_DIR = claudeConfigDir
process.env.CLAUDE_PLUGINS_HOME = pluginsHome
process.env.CLAUDE_SETTINGS_PATH = settingsPath
process.env.OPENCODE_CONFIG_DIR = opencodeConfigDir
}
describe("auto-slash command executor plugin dispatch", () => {
let tempDir = ""
let envSnapshot: EnvSnapshot
beforeEach(() => {
tempDir = mkdtempSync(join(tmpdir(), "omo-executor-plugin-test-"))
envSnapshot = {
CLAUDE_CONFIG_DIR: process.env.CLAUDE_CONFIG_DIR,
CLAUDE_PLUGINS_HOME: process.env.CLAUDE_PLUGINS_HOME,
CLAUDE_SETTINGS_PATH: process.env.CLAUDE_SETTINGS_PATH,
OPENCODE_CONFIG_DIR: process.env.OPENCODE_CONFIG_DIR,
}
writePluginFixture(tempDir)
})
afterEach(() => {
for (const key of ENV_KEYS) {
const previousValue = envSnapshot[key]
if (previousValue === undefined) {
delete process.env[key]
} else {
process.env[key] = previousValue
}
}
rmSync(tempDir, { recursive: true, force: true })
})
it("resolves marketplace plugin commands when plugin loading is enabled", async () => {
const result = await executeSlashCommand(
{
command: "daplug:run-prompt",
args: "ship it",
raw: "/daplug:run-prompt ship it",
},
{
skills: [],
pluginsEnabled: true,
},
)
expect(result.success).toBe(true)
expect(result.replacementText).toContain("# /daplug:run-prompt Command")
expect(result.replacementText).toContain("**Scope**: plugin")
})
it("excludes marketplace commands when plugins are disabled via config toggle", async () => {
const result = await executeSlashCommand(
{
command: "daplug:run-prompt",
args: "",
raw: "/daplug:run-prompt",
},
{
skills: [],
pluginsEnabled: false,
},
)
expect(result.success).toBe(false)
expect(result.error).toBe(
'Command "/daplug:run-prompt" not found. Use the skill tool to list available skills and commands.',
)
})
it("returns standard not-found for unknown namespaced commands", async () => {
const result = await executeSlashCommand(
{
command: "daplug:missing",
args: "",
raw: "/daplug:missing",
},
{
skills: [],
pluginsEnabled: true,
},
)
expect(result.success).toBe(false)
expect(result.error).toBe(
'Command "/daplug:missing" not found. Use the skill tool to list available skills and commands.',
)
expect(result.error).not.toContain("Marketplace plugin commands")
})
it("replaces $ARGUMENTS placeholders in plugin command templates", async () => {
const result = await executeSlashCommand(
{
command: "daplug:templated",
args: "ship it",
raw: "/daplug:templated ship it",
},
{
skills: [],
pluginsEnabled: true,
},
)
expect(result.success).toBe(true)
expect(result.replacementText).toContain("Echo ship it and ship it.")
expect(result.replacementText).not.toContain("$ARGUMENTS")
expect(result.replacementText).not.toContain("${user_message}")
})
})

View File

@@ -7,6 +7,7 @@ import {
sanitizeModelField,
getClaudeConfigDir,
getOpenCodeConfigDir,
discoverPluginCommandDefinitions,
} from "../../shared"
import { loadBuiltinCommands } from "../../features/builtin-commands"
import type { CommandFrontmatter } from "../../features/claude-code-command-loader/types"
@@ -15,7 +16,7 @@ import { discoverAllSkills, type LoadedSkill, type LazyContentLoader } from "../
import type { ParsedSlashCommand } from "./types"
interface CommandScope {
type: "user" | "project" | "opencode" | "opencode-project" | "skill" | "builtin"
type: "user" | "project" | "opencode" | "opencode-project" | "skill" | "builtin" | "plugin"
}
interface CommandMetadata {
@@ -99,6 +100,25 @@ function skillToCommandInfo(skill: LoadedSkill): CommandInfo {
export interface ExecutorOptions {
skills?: LoadedSkill[]
pluginsEnabled?: boolean
enabledPluginsOverride?: Record<string, boolean>
}
function discoverPluginCommands(options?: ExecutorOptions): CommandInfo[] {
const pluginDefinitions = discoverPluginCommandDefinitions(options)
return Object.entries(pluginDefinitions).map(([name, definition]) => ({
name,
metadata: {
name,
description: definition.description || "",
model: definition.model,
agent: definition.agent,
subtask: definition.subtask,
},
content: definition.template,
scope: "plugin",
}))
}
async function discoverAllCommands(options?: ExecutorOptions): Promise<CommandInfo[]> {
@@ -128,6 +148,7 @@ async function discoverAllCommands(options?: ExecutorOptions): Promise<CommandIn
const skills = options?.skills ?? await discoverAllSkills()
const skillCommands = skills.map(skillToCommandInfo)
const pluginCommands = discoverPluginCommands(options)
return [
...builtinCommands,
@@ -136,6 +157,7 @@ async function discoverAllCommands(options?: ExecutorOptions): Promise<CommandIn
...opencodeGlobalCommands,
...userCommands,
...skillCommands,
...pluginCommands,
]
}
@@ -179,7 +201,11 @@ async function formatCommandTemplate(cmd: CommandInfo, args: string): Promise<st
const commandDir = cmd.path ? dirname(cmd.path) : process.cwd()
const withFileRefs = await resolveFileReferencesInText(content, commandDir)
const resolvedContent = await resolveCommandsInText(withFileRefs)
sections.push(resolvedContent.trim())
const resolvedArguments = args
const substitutedContent = resolvedContent
.replace(/\$\{user_message\}/g, resolvedArguments)
.replace(/\$ARGUMENTS/g, resolvedArguments)
sections.push(substitutedContent.trim())
if (args) {
sections.push("\n\n---\n")
@@ -202,9 +228,7 @@ export async function executeSlashCommand(parsed: ParsedSlashCommand, options?:
if (!command) {
return {
success: false,
error: parsed.command.includes(":")
? `Marketplace plugin commands like "/${parsed.command}" are not supported. Use .claude/commands/ for custom commands.`
: `Command "/${parsed.command}" not found. Use the skill tool to list available skills and commands.`,
error: `Command "/${parsed.command}" not found. Use the skill tool to list available skills and commands.`,
}
}

View File

@@ -22,11 +22,15 @@ const sessionProcessedCommandExecutions = new Set<string>()
export interface AutoSlashCommandHookOptions {
skills?: LoadedSkill[]
pluginsEnabled?: boolean
enabledPluginsOverride?: Record<string, boolean>
}
export function createAutoSlashCommandHook(options?: AutoSlashCommandHookOptions) {
const executorOptions: ExecutorOptions = {
skills: options?.skills,
pluginsEnabled: options?.pluginsEnabled,
enabledPluginsOverride: options?.enabledPluginsOverride,
}
return {

View File

@@ -1,6 +1,6 @@
# src/hooks/claude-code-hooks/ — Claude Code Compatibility
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,6 +1,6 @@
# src/hooks/keyword-detector/ — Mode Keyword Injection
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,14 +1,53 @@
import { beforeEach, describe, expect, test } from "bun:test"
declare const require: (name: string) => any
const { beforeEach, describe, expect, mock, test } = require("bun:test")
const readConnectedProvidersCacheMock = mock(() => null)
const readProviderModelsCacheMock = mock(() => null)
const transformModelForProviderMock = mock((provider: string, model: string) => {
if (provider === "github-copilot") {
return model
.replace("claude-opus-4-6", "claude-opus-4.6")
.replace("claude-sonnet-4-6", "claude-sonnet-4.6")
.replace("claude-sonnet-4-5", "claude-sonnet-4.5")
.replace("claude-haiku-4-5", "claude-haiku-4.5")
.replace("claude-sonnet-4", "claude-sonnet-4")
.replace(/gemini-3\.1-pro(?!-)/g, "gemini-3.1-pro-preview")
.replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview")
}
if (provider === "google") {
return model
.replace(/gemini-3\.1-pro(?!-)/g, "gemini-3.1-pro-preview")
.replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview")
}
return model
})
mock.module("../../shared/connected-providers-cache", () => ({
readConnectedProvidersCache: readConnectedProvidersCacheMock,
readProviderModelsCache: readProviderModelsCacheMock,
}))
mock.module("../../shared/provider-model-id-transform", () => ({
transformModelForProvider: transformModelForProviderMock,
}))
import {
clearPendingModelFallback,
createModelFallbackHook,
setSessionFallbackChain,
setPendingModelFallback,
} from "./hook"
describe("model fallback hook", () => {
beforeEach(() => {
readConnectedProvidersCacheMock.mockReturnValue(null)
readProviderModelsCacheMock.mockReturnValue(null)
readConnectedProvidersCacheMock.mockClear()
readProviderModelsCacheMock.mockClear()
clearPendingModelFallback("ses_model_fallback_main")
clearPendingModelFallback("ses_model_fallback_ghcp")
clearPendingModelFallback("ses_model_fallback_google")
})
test("applies pending fallback on chat.message by overriding model", async () => {
@@ -95,8 +134,8 @@ describe("model fallback hook", () => {
//#then - chain should progress to entry[1], not repeat entry[0]
expect(secondOutput.message["model"]).toEqual({
providerID: "opencode",
modelID: "kimi-k2.5-free",
providerID: "zai-coding-plan",
modelID: "glm-5",
})
expect(secondOutput.message["variant"]).toBeUndefined()
})
@@ -138,4 +177,92 @@ describe("model fallback hook", () => {
expect(toastCalls.length).toBe(1)
expect(toastCalls[0]?.title).toBe("Model fallback")
})
test("transforms model names for github-copilot provider via fallback chain", async () => {
//#given
const sessionID = "ses_model_fallback_ghcp"
clearPendingModelFallback(sessionID)
const hook = createModelFallbackHook() as unknown as {
"chat.message"?: (
input: { sessionID: string },
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
) => Promise<void>
}
// Set a custom fallback chain that routes through github-copilot
setSessionFallbackChain(sessionID, [
{ providers: ["github-copilot"], model: "claude-sonnet-4-6" },
])
const set = setPendingModelFallback(
sessionID,
"Atlas (Plan Executor)",
"github-copilot",
"claude-sonnet-4-6",
)
expect(set).toBe(true)
const output = {
message: {
model: { providerID: "github-copilot", modelID: "claude-sonnet-4-6" },
},
parts: [{ type: "text", text: "continue" }],
}
//#when
await hook["chat.message"]?.({ sessionID }, output)
//#then — model name should be transformed from hyphen to dot notation
expect(output.message["model"]).toEqual({
providerID: "github-copilot",
modelID: "claude-sonnet-4.6",
})
clearPendingModelFallback(sessionID)
})
test("transforms model names for google provider via fallback chain", async () => {
//#given
const sessionID = "ses_model_fallback_google"
clearPendingModelFallback(sessionID)
const hook = createModelFallbackHook() as unknown as {
"chat.message"?: (
input: { sessionID: string },
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
) => Promise<void>
}
// Set a custom fallback chain that routes through google
setSessionFallbackChain(sessionID, [
{ providers: ["google"], model: "gemini-3-pro" },
])
const set = setPendingModelFallback(
sessionID,
"Oracle",
"google",
"gemini-3-pro",
)
expect(set).toBe(true)
const output = {
message: {
model: { providerID: "google", modelID: "gemini-3-pro" },
},
parts: [{ type: "text", text: "continue" }],
}
//#when
await hook["chat.message"]?.({ sessionID }, output)
//#then — model name should remain gemini-3-pro because no google transform exists for this ID
expect(output.message["model"]).toEqual({
providerID: "google",
modelID: "gemini-3-pro",
})
clearPendingModelFallback(sessionID)
})
})

View File

@@ -3,6 +3,7 @@ import { getAgentConfigKey } from "../../shared/agent-display-names"
import { AGENT_MODEL_REQUIREMENTS } from "../../shared/model-requirements"
import { readConnectedProvidersCache, readProviderModelsCache } from "../../shared/connected-providers-cache"
import { selectFallbackProvider } from "../../shared/model-error-classifier"
import { transformModelForProvider } from "../../shared/provider-model-id-transform"
import { log } from "../../shared/logger"
import { getTaskToastManager } from "../../features/task-toast-manager"
import type { ChatMessageInput, ChatMessageHandlerOutput } from "../../plugin/chat-message"
@@ -145,7 +146,7 @@ export function getNextFallback(
return {
providerID,
modelID: fallback.model,
modelID: transformModelForProvider(providerID, fallback.model),
variant: fallback.variant,
}
}

View File

@@ -414,4 +414,157 @@ describe("preemptive-compaction", () => {
restoreTimeouts()
}
})
// #given first compaction succeeded and context grew again
// #when tool.execute.after runs after new high-token message
// #then should trigger compaction again (re-compaction)
it("should allow re-compaction when context grows after successful compaction", async () => {
const hook = createPreemptiveCompactionHook(ctx as never, {} as never)
const sessionID = "ses_recompact"
// given - first compaction cycle
await hook.event({
event: {
type: "message.updated",
properties: {
info: {
role: "assistant",
sessionID,
providerID: "anthropic",
modelID: "claude-sonnet-4-6",
finish: true,
tokens: {
input: 170000,
output: 0,
reasoning: 0,
cache: { read: 10000, write: 0 },
},
},
},
},
})
await hook["tool.execute.after"](
{ tool: "bash", sessionID, callID: "call_1" },
{ title: "", output: "test", metadata: null }
)
expect(ctx.client.session.summarize).toHaveBeenCalledTimes(1)
// when - new message with high tokens (context grew after compaction)
await hook.event({
event: {
type: "message.updated",
properties: {
info: {
role: "assistant",
sessionID,
providerID: "anthropic",
modelID: "claude-sonnet-4-6",
finish: true,
tokens: {
input: 170000,
output: 0,
reasoning: 0,
cache: { read: 10000, write: 0 },
},
},
},
},
})
await hook["tool.execute.after"](
{ tool: "bash", sessionID, callID: "call_2" },
{ title: "", output: "test", metadata: null }
)
// then - summarize should fire again
expect(ctx.client.session.summarize).toHaveBeenCalledTimes(2)
})
// #given modelContextLimitsCache has model-specific limit (256k)
// #when tokens are above default 78% of 200k but below 78% of 256k
// #then should NOT trigger compaction
it("should use model-specific context limit from modelContextLimitsCache", async () => {
const modelContextLimitsCache = new Map<string, number>()
modelContextLimitsCache.set("opencode/kimi-k2.5-free", 262144)
const hook = createPreemptiveCompactionHook(ctx as never, {} as never, {
anthropicContext1MEnabled: false,
modelContextLimitsCache,
})
const sessionID = "ses_kimi_limit"
// 180k total tokens — above 78% of 200k (156k) but below 78% of 256k (204k)
await hook.event({
event: {
type: "message.updated",
properties: {
info: {
role: "assistant",
sessionID,
providerID: "opencode",
modelID: "kimi-k2.5-free",
finish: true,
tokens: {
input: 170000,
output: 0,
reasoning: 0,
cache: { read: 10000, write: 0 },
},
},
},
},
})
await hook["tool.execute.after"](
{ tool: "bash", sessionID, callID: "call_1" },
{ title: "", output: "test", metadata: null }
)
expect(ctx.client.session.summarize).not.toHaveBeenCalled()
})
// #given modelContextLimitsCache has model-specific limit (256k)
// #when tokens exceed 78% of model-specific limit
// #then should trigger compaction
it("should trigger compaction at model-specific threshold", async () => {
const modelContextLimitsCache = new Map<string, number>()
modelContextLimitsCache.set("opencode/kimi-k2.5-free", 262144)
const hook = createPreemptiveCompactionHook(ctx as never, {} as never, {
anthropicContext1MEnabled: false,
modelContextLimitsCache,
})
const sessionID = "ses_kimi_trigger"
// 210k total — above 78% of 256k (≈204k)
await hook.event({
event: {
type: "message.updated",
properties: {
info: {
role: "assistant",
sessionID,
providerID: "opencode",
modelID: "kimi-k2.5-free",
finish: true,
tokens: {
input: 200000,
output: 0,
reasoning: 0,
cache: { read: 10000, write: 0 },
},
},
},
},
})
await hook["tool.execute.after"](
{ tool: "bash", sessionID, callID: "call_1" },
{ title: "", output: "test", metadata: null }
)
expect(ctx.client.session.summarize).toHaveBeenCalled()
})
})

View File

@@ -7,6 +7,7 @@ const PREEMPTIVE_COMPACTION_TIMEOUT_MS = 120_000
type ModelCacheStateLike = {
anthropicContext1MEnabled: boolean
modelContextLimitsCache?: Map<string, number>
}
function getAnthropicActualLimit(modelCacheState?: ModelCacheStateLike): number {
@@ -91,10 +92,12 @@ export function createPreemptiveCompactionHook(
const cached = tokenCache.get(sessionID)
if (!cached) return
const actualLimit =
isAnthropicProvider(cached.providerID)
? getAnthropicActualLimit(modelCacheState)
: DEFAULT_ACTUAL_LIMIT
const modelSpecificLimit = !isAnthropicProvider(cached.providerID)
? modelCacheState?.modelContextLimitsCache?.get(`${cached.providerID}/${cached.modelID}`)
: undefined
const actualLimit = isAnthropicProvider(cached.providerID)
? getAnthropicActualLimit(modelCacheState)
: modelSpecificLimit ?? DEFAULT_ACTUAL_LIMIT
const lastTokens = cached.tokens
const totalInputTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0)
@@ -164,6 +167,7 @@ export function createPreemptiveCompactionHook(
modelID: info.modelID ?? "",
tokens: info.tokens,
})
compactedSessions.delete(info.sessionID)
}
}

View File

@@ -1,6 +1,6 @@
# src/hooks/ralph-loop/ — Self-Referential Dev Loop
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,6 +1,6 @@
# src/hooks/rules-injector/ — Conditional Rules Injection
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -7,6 +7,7 @@ import {
getAfplayPath,
getPaplayPath,
getAplayPath,
getTerminalNotifierPath,
} from "./session-notification-utils"
import { buildWindowsToastScript, escapeAppleScriptText, escapePowerShellSingleQuotedText } from "./session-notification-formatting"
@@ -39,6 +40,22 @@ export async function sendSessionNotification(
): Promise<void> {
switch (platform) {
case "darwin": {
// Try terminal-notifier first — deterministic click-to-focus
const terminalNotifierPath = await getTerminalNotifierPath()
if (terminalNotifierPath) {
const bundleId = process.env.__CFBundleIdentifier
try {
if (bundleId) {
await ctx.$`${terminalNotifierPath} -title ${title} -message ${message} -activate ${bundleId}`
} else {
await ctx.$`${terminalNotifierPath} -title ${title} -message ${message}`
}
break
} catch {
}
}
// Fallback: osascript (click may open Finder instead of terminal)
const osascriptPath = await getOsascriptPath()
if (!osascriptPath) return

View File

@@ -32,11 +32,13 @@ export const getPowershellPath = createCommandFinder("powershell")
export const getAfplayPath = createCommandFinder("afplay")
export const getPaplayPath = createCommandFinder("paplay")
export const getAplayPath = createCommandFinder("aplay")
export const getTerminalNotifierPath = createCommandFinder("terminal-notifier")
export function startBackgroundCheck(platform: Platform): void {
if (platform === "darwin") {
getOsascriptPath().catch(() => {})
getAfplayPath().catch(() => {})
getTerminalNotifierPath().catch(() => {})
} else if (platform === "linux") {
getNotifySendPath().catch(() => {})
getPaplayPath().catch(() => {})

View File

@@ -365,4 +365,148 @@ describe("session-notification", () => {
// then - only one notification should be sent
expect(notificationCalls).toHaveLength(1)
})
function createSenderMockCtx() {
const notifyCalls: string[] = []
const mockCtx = {
$: async (cmd: TemplateStringsArray | string, ...values: any[]) => {
const cmdStr = typeof cmd === "string"
? cmd
: cmd.reduce((acc, part, i) => acc + part + (values[i] ?? ""), "")
notifyCalls.push(cmdStr)
return { stdout: "", stderr: "", exitCode: 0 }
},
} as any
return { mockCtx, notifyCalls }
}
test("should use terminal-notifier with -activate when available on darwin", async () => {
// given - terminal-notifier is available and __CFBundleIdentifier is set
spyOn(sender, "sendSessionNotification").mockRestore()
const { mockCtx, notifyCalls } = createSenderMockCtx()
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue("/usr/local/bin/terminal-notifier")
const originalEnv = process.env.__CFBundleIdentifier
process.env.__CFBundleIdentifier = "com.mitchellh.ghostty"
try {
// when - sendSessionNotification is called directly on darwin
await sender.sendSessionNotification(mockCtx, "darwin", "Test Title", "Test Message")
// then - notification uses terminal-notifier with -activate flag
expect(notifyCalls.length).toBeGreaterThanOrEqual(1)
const tnCall = notifyCalls.find(c => c.includes("terminal-notifier"))
expect(tnCall).toBeDefined()
expect(tnCall).toContain("-activate")
expect(tnCall).toContain("com.mitchellh.ghostty")
} finally {
if (originalEnv !== undefined) {
process.env.__CFBundleIdentifier = originalEnv
} else {
delete process.env.__CFBundleIdentifier
}
}
})
test("should fall back to osascript when terminal-notifier is not available", async () => {
// given - terminal-notifier is NOT available
spyOn(sender, "sendSessionNotification").mockRestore()
const { mockCtx, notifyCalls } = createSenderMockCtx()
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue(null)
spyOn(utils, "getOsascriptPath").mockResolvedValue("/usr/bin/osascript")
// when - sendSessionNotification is called directly on darwin
await sender.sendSessionNotification(mockCtx, "darwin", "Test Title", "Test Message")
// then - notification uses osascript (fallback)
expect(notifyCalls.length).toBeGreaterThanOrEqual(1)
const osascriptCall = notifyCalls.find(c => c.includes("osascript"))
expect(osascriptCall).toBeDefined()
const tnCall = notifyCalls.find(c => c.includes("terminal-notifier"))
expect(tnCall).toBeUndefined()
})
test("should fall back to osascript when terminal-notifier execution fails", async () => {
// given - terminal-notifier exists but invocation fails
spyOn(sender, "sendSessionNotification").mockRestore()
const notifyCalls: string[] = []
const mockCtx = {
$: async (cmd: TemplateStringsArray | string, ...values: unknown[]) => {
const cmdStr = typeof cmd === "string"
? cmd
: cmd.reduce((acc, part, index) => `${acc}${part}${String(values[index] ?? "")}`, "")
notifyCalls.push(cmdStr)
if (cmdStr.includes("terminal-notifier")) {
throw new Error("terminal-notifier failed")
}
return { stdout: "", stderr: "", exitCode: 0 }
},
} as any
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue("/usr/local/bin/terminal-notifier")
spyOn(utils, "getOsascriptPath").mockResolvedValue("/usr/bin/osascript")
// when - sendSessionNotification is called directly on darwin
await sender.sendSessionNotification(mockCtx, "darwin", "Test Title", "Test Message")
// then - osascript fallback should be attempted after terminal-notifier failure
const tnCall = notifyCalls.find(c => c.includes("terminal-notifier"))
const osascriptCall = notifyCalls.find(c => c.includes("osascript"))
expect(tnCall).toBeDefined()
expect(osascriptCall).toBeDefined()
})
test("should invoke terminal-notifier without array interpolation", async () => {
// given - shell interpolation rejects array values
spyOn(sender, "sendSessionNotification").mockRestore()
const notifyCalls: string[] = []
const mockCtx = {
$: async (cmd: TemplateStringsArray | string, ...values: unknown[]) => {
if (values.some(Array.isArray)) {
throw new Error("array interpolation unsupported")
}
const commandString = typeof cmd === "string"
? cmd
: cmd.reduce((acc, part, index) => `${acc}${part}${String(values[index] ?? "")}`, "")
notifyCalls.push(commandString)
return { stdout: "", stderr: "", exitCode: 0 }
},
} as any
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue("/usr/local/bin/terminal-notifier")
spyOn(utils, "getOsascriptPath").mockResolvedValue("/usr/bin/osascript")
// when - terminal-notifier command is executed
await sender.sendSessionNotification(mockCtx, "darwin", "Test Title", "Test Message")
// then - terminal-notifier succeeds directly and fallback is not used
const tnCall = notifyCalls.find(c => c.includes("terminal-notifier"))
const osascriptCall = notifyCalls.find(c => c.includes("osascript"))
expect(tnCall).toBeDefined()
expect(osascriptCall).toBeUndefined()
})
test("should use terminal-notifier without -activate when __CFBundleIdentifier is not set", async () => {
// given - terminal-notifier available but no bundle ID
spyOn(sender, "sendSessionNotification").mockRestore()
const { mockCtx, notifyCalls } = createSenderMockCtx()
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue("/usr/local/bin/terminal-notifier")
const originalEnv = process.env.__CFBundleIdentifier
delete process.env.__CFBundleIdentifier
try {
// when - sendSessionNotification is called directly on darwin
await sender.sendSessionNotification(mockCtx, "darwin", "Test Title", "Test Message")
// then - terminal-notifier used but without -activate flag
expect(notifyCalls.length).toBeGreaterThanOrEqual(1)
const tnCall = notifyCalls.find(c => c.includes("terminal-notifier"))
expect(tnCall).toBeDefined()
expect(tnCall).not.toContain("-activate")
} finally {
if (originalEnv !== undefined) {
process.env.__CFBundleIdentifier = originalEnv
}
}
})
})

View File

@@ -1,6 +1,6 @@
# src/hooks/session-recovery/ — Auto Session Error Recovery
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -16,6 +16,8 @@
* inconsistencies defensively while maintaining backwards compatibility.
*/
import { normalizeModelID } from "../../shared"
/**
* Extracts provider-specific prefix from model ID (if present).
* Custom providers may use prefixes for routing (e.g., vertex_ai/, openai/).
@@ -36,24 +38,6 @@ function extractModelPrefix(modelID: string): { prefix: string; base: string } {
}
}
/**
* Normalizes model IDs to use consistent hyphen formatting.
* GitHub Copilot may use dots (claude-opus-4.6) but our maps use hyphens (claude-opus-4-6).
* This ensures lookups work regardless of format.
*
* @example
* normalizeModelID("claude-opus-4.6") // "claude-opus-4-6"
* normalizeModelID("gemini-3.5-pro") // "gemini-3-5-pro"
* normalizeModelID("gpt-5.2") // "gpt-5-2"
* normalizeModelID("vertex_ai/claude-opus-4.6") // "vertex_ai/claude-opus-4-6"
*/
function normalizeModelID(modelID: string): string {
// Replace dots with hyphens when followed by a digit
// This handles version numbers like 4.5 → 4-5, 5.2 → 5-2
return modelID.replace(/\.(\d+)/g, "-$1")
}
// Maps model IDs to their "high reasoning" variant (internal convention)
// For OpenAI models, this signals that reasoning_effort should be set to "high"

View File

@@ -1,6 +1,6 @@
# src/hooks/todo-continuation-enforcer/ — Boulder Continuation Mechanism
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,3 +1,4 @@
import { initConfigContext } from "./cli/config-manager/config-context"
import type { Plugin } from "@opencode-ai/plugin"
import type { HookName } from "./config"
@@ -14,6 +15,8 @@ import { injectServerAuthIntoClient, log } from "./shared"
import { startTmuxCheck } from "./tools"
const OhMyOpenCodePlugin: Plugin = async (ctx) => {
// Initialize config context for plugin runtime (prevents warnings from hooks)
initConfigContext("opencode", null)
log("[OhMyOpenCodePlugin] ENTRY - plugin loading", {
directory: ctx.directory,
})

View File

@@ -1,6 +1,6 @@
# src/mcp/ — 3 Built-in Remote MCPs
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -1,6 +1,6 @@
# src/plugin-handlers/ — 6-Phase Config Loading Pipeline
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -99,9 +99,9 @@ export function applyToolConfig(params: {
}
params.config.permission = {
...(params.config.permission as Record<string, unknown>),
webfetch: "allow",
external_directory: "allow",
...(params.config.permission as Record<string, unknown>),
task: "deny",
};
}

View File

@@ -1,6 +1,6 @@
# src/plugin/ — 8 OpenCode Hook Handlers + Hook Composition
**Generated:** 2026-02-24
**Generated:** 2026-03-02
## OVERVIEW

View File

@@ -106,4 +106,41 @@ describe("createChatHeadersHandler", () => {
expect(output.headers["x-initiator"]).toBeUndefined()
})
test("skips x-initiator override when model uses @ai-sdk/github-copilot", async () => {
const handler = createChatHeadersHandler({
ctx: {
client: {
session: {
message: async () => ({
data: {
parts: [
{
type: "text",
text: `notification\n${OMO_INTERNAL_INITIATOR_MARKER}`,
},
],
},
}),
},
},
} as never,
})
const output: { headers: Record<string, string> } = { headers: {} }
await handler(
{
sessionID: "ses_4",
provider: { id: "github-copilot" },
model: { api: { npm: "@ai-sdk/github-copilot" } },
message: {
id: "msg_4",
role: "user",
},
},
output,
)
expect(output.headers["x-initiator"]).toBeUndefined()
})
})

View File

@@ -123,6 +123,17 @@ export function createChatHeadersHandler(args: { ctx: PluginContext }): (input:
if (!isChatHeadersOutput(output)) return
if (!isCopilotProvider(normalizedInput.provider.id)) return
// Do not override x-initiator when @ai-sdk/github-copilot is active.
// OpenCode's copilot fetch wrapper already sets x-initiator based on
// the actual request body content. Overriding it here causes a mismatch
// that the Copilot API rejects with "invalid initiator".
const model = isRecord(input) && isRecord((input as Record<string, unknown>).model)
? (input as Record<string, unknown>).model as Record<string, unknown>
: undefined
const api = model && isRecord(model.api) ? model.api as Record<string, unknown> : undefined
if (api?.npm === "@ai-sdk/github-copilot") return
if (!(await isOmoInternalMessage(normalizedInput, ctx.client))) return
output.headers["x-initiator"] = "agent"

View File

@@ -334,8 +334,8 @@ describe("createEventHandler - model fallback", () => {
//#then - second fallback entry applied (chain advanced)
expect(second.message["model"]).toEqual({
providerID: "opencode",
modelID: "kimi-k2.5-free",
providerID: "zai-coding-plan",
modelID: "glm-5",
})
expect(second.message["variant"]).toBeUndefined()
expect(abortCalls).toEqual([sessionID, sessionID])

View File

@@ -111,6 +111,7 @@ export function createContinuationHooks(args: {
isContinuationStopped: (sessionID: string) =>
stopContinuationGuard?.isStopped(sessionID) ?? false,
agentOverrides: pluginConfig.agents,
autoCommit: pluginConfig.start_work?.auto_commit,
}))
: null

View File

@@ -1,5 +1,5 @@
import type { AvailableSkill } from "../../agents/dynamic-agent-prompt-builder"
import type { HookName } from "../../config"
import type { HookName, OhMyOpenCodeConfig } from "../../config"
import type { LoadedSkill } from "../../features/opencode-skill-loader/types"
import type { PluginContext } from "../types"
@@ -13,12 +13,20 @@ export type SkillHooks = {
export function createSkillHooks(args: {
ctx: PluginContext
pluginConfig: OhMyOpenCodeConfig
isHookEnabled: (hookName: HookName) => boolean
safeHookEnabled: boolean
mergedSkills: LoadedSkill[]
availableSkills: AvailableSkill[]
}): SkillHooks {
const { ctx, isHookEnabled, safeHookEnabled, mergedSkills, availableSkills } = args
const {
ctx,
pluginConfig,
isHookEnabled,
safeHookEnabled,
mergedSkills,
availableSkills,
} = args
const safeHook = <T>(hookName: HookName, factory: () => T): T | null =>
safeCreateHook(hookName, factory, { enabled: safeHookEnabled })
@@ -30,7 +38,11 @@ export function createSkillHooks(args: {
const autoSlashCommand = isHookEnabled("auto-slash-command")
? safeHook("auto-slash-command", () =>
createAutoSlashCommandHook({ skills: mergedSkills }))
createAutoSlashCommandHook({
skills: mergedSkills,
pluginsEnabled: pluginConfig.claude_code?.plugins ?? true,
enabledPluginsOverride: pluginConfig.claude_code?.plugins_override,
}))
: null
return { categorySkillReminder, autoSlashCommand }

View File

@@ -1,5 +1,5 @@
export function createSystemTransformHandler(): (
input: { sessionID: string },
input: { sessionID?: string; model: { id: string; providerID: string; [key: string]: unknown } },
output: { system: string[] },
) => Promise<void> {
return async (): Promise<void> => {}

Some files were not shown because too many files have changed in this diff Show More