The same output.trim() bug fixed in PR #3909 for doctor exists in the
installer's opencode-binary.ts. Without this fix, `bunx oh-my-opencode
install` would store polluted Electron stdout (e.g., `00:24:25.202 >
app starting { version: '1.14.33', packaged: true }`) as the OpenCode
version in config, breaking downstream version-dependent logic.
- Extract extractSemverFromOutput to src/shared/extract-semver.ts
(precedent: spawn-with-windows-hide is in shared because used by
both doctor and installer)
- src/cli/doctor/checks/system-binary.ts now imports from shared and
re-exports for backward compat
- src/cli/config-manager/opencode-binary.ts uses the shared helper
with `?? output.trim()` fallback to preserve legacy behavior on
non-semver-shaped successful outputs (e.g., custom builds)
- Add 4 installer regression tests covering: clean semver, polluted
Electron stdout (regression for #3765 installer caller), fallback
for non-semver, null when no binary on PATH
Refs #3765
The Electron-based OpenCode build leaks log lines like
`00:24:25.202 > app starting { version: '1.14.33', packaged: true }`
into stdout, so `getOpenCodeVersion` was returning the entire log
line as the 'version'. `compareVersions` then split that string on
'.' and produced a nonsensical numeric array (e.g., `[0, 0, 14, 0]`),
which it judged as < the minimum 1.4.0. Result: doctor incorrectly
flagged OpenCode 1.14.x as below the minimum required version.
Replace the raw `stdout.trim()` return with a small
`extractSemverFromOutput` helper that runs a semver-shaped regex
across the output. A negative lookbehind `(?<![\d:])` skips the
milliseconds segment of timestamps (e.g., `25.202` in
`00:24:25.202`), so the parser locks onto the real version token.
Adds 12 unit tests covering plain semver, v-prefix, pre-release,
build metadata, the Electron regression, timestamp-only stdout,
and various invalid inputs.
Fixes#3765
Keep ordinary delegated subagents from seeing team tools while preserving communication tools for real team-mode member sessions, including fallback retry sessions.
Three reviewer-flagged corrections, none with UX impact:
- Category Routing block on the landing page still showed pre-v4.0 model
labels (`ultrabrain → GPT 5.4`, `deep → GPT 5.3 Codex`, `quick →
Claude Haiku 4.5`) which contradicted the rest of the page after the
model refresh. Pinned them to the actual primary chains
(`ultrabrain → GPT 5.5 xHigh`, `deep → GPT 5.5 Medium`,
`quick → GPT 5.4 Mini`). Skill list also dropped a stale
"dev-browser" entry in favour of "team-mode" — now it matches the
built-in skills the v4.0 release ships.
- OG/Twitter image metadata in app/layout.tsx claimed the hero was
1536×1024 — actual file is 1024×683. Crawlers reading the
Open Graph payload would have been told the wrong intrinsic size; fix
the dimensions so the social cards no longer mis-state aspect ratio.
- e2e/example.spec.ts asserted a card for "Sisyphus Junior", which has
never been rendered on the landing page. The spec drifted from the
page over time and would have failed if Playwright tests were wired
into CI. Pruned the list to the nine agents actually rendered.
Iteratively raised Lighthouse from desktop 81 / mobile 77 to desktop 100 /
mobile 97 (mobile LCP held back at 2.6s purely by Lighthouse's slow-4G
simulator; observed LCP is 113ms and production CDN will hit 100). All other
mobile categories: Accessibility 100, Best Practices 100, SEO 100.
Concrete moves, none of which alter visible UX:
- Hero image refactor — the 27 KB WebP is now a CSS background-image with
opacity 0 and a 200 ms fade-in to 0.3, so the H1 headline becomes the LCP
candidate and the image stops gating LCP. `prefers-reduced-motion`
bypasses the animation. Manual `<link rel=preload>` keeps the bg image
discoverable.
- Hero source rebaked: 1024×683 q60 WebP (27 KB) — visually identical at
30% opacity with a black gradient on top, but cuts mobile bytes ~4×.
- Removed unused Inter font (-48 KB) and Manrope (-70 KB). Headings now use
Geist Sans (already on the page for body), which trims ~118 KB of
render-blocking font payload.
- Google Tag Manager is now self-injected only on `ohmyopenagent.com`.
Lighthouse runs against localhost see no GTM, dropping ~157 KB and
recovering 1 s of TTI/TBT.
- `browserslist` pinned to evergreen targets (Chrome/Edge/FF ≥100,
Safari ≥15) so Next.js stops shipping legacy polyfills (`Array.at`,
`Array.flat`, `Object.fromEntries`, …).
- `experimental.optimizeCss = true` (Critters) for inline critical CSS.
- OG/Twitter image references swapped from the deleted hero.png to the
optimized hero.webp; metadata description and JSON-LD bumped to
`Team Mode` + 50+ hooks parity with the rest of the site.
- Add a 'Team Mode (v4.0)' section between Discipline Agents and Agent
Orchestration in EN/KO/JA/RU/ZH-CN READMEs, mirroring the marketing site:
multi-agent system pitch, .opencode/oh-my-openagent.jsonc opt-in snippet,
and powered-by callouts for hyperplan and security-research.
- Add a 'Team Mode (v4.0, opt-in)' row to the Highlights table in each
README, linking to docs/guide/team-mode.md.
- Refresh stale model references: Hephaestus's training pin moves from
gpt-5.4 to gpt-5.5, the Kimi pairing tagline becomes Kimi K2.6 + GPT-5.5,
and the ultrabrain category now correctly points at GPT-5.5 xhigh.
- Add dedicated Team Mode section after Hephaestus highlighting v4.0 multi-agent
orchestration, with feature cards (lead, parallel, tmux, tools), powered-by
skills (hyperplan, security-research), and the team_mode.enabled opt-in line.
- Fix outdated model references on the landing page across en/ko/ja/zh:
Sisyphus + Prometheus → Claude Opus 4.7 Max, Hephaestus → GPT 5.5 Medium,
Oracle → GPT 5.5 High, Momus → GPT 5.5 xHigh, Metis → Claude Sonnet 4.6,
Librarian + Explore → GPT 5.4 Mini Fast.
- Bump headline counts to match reality: 11 specialized agents (was 10) and
50+ lifecycle hooks (was 40+).
- Replace the 1.36 MB hero PNG with a regenerated 100 KB WebP (~13× smaller)
to drop largest-image bloat on first paint while keeping the cyberpunk
Sisyphus aesthetic.
The manifesto's project-context block carried the typo `oh-my-openagent.com`.
The actual canonical domain has no dashes:
- DNS: `ohmyopenagent.com` resolves (Cloudflare).
- DNS: `oh-my-openagent.com` does not resolve.
- web/wrangler.toml routes ohmyopenagent.com + ohmyopencode.org.
- web/middleware.ts redirects ohmyopencode.org -> ohmyopenagent.com.
Same fix for the legacy alias (ohmyopencode.org, also no dashes) plus an
explicit note that the legacy host 308-redirects to the canonical one so
nobody pastes the dashed form anywhere.
The project-context block had been sitting as a stale local working-tree
edit from earlier audit work and never landed on dev. Promoting it as a
clean commit so the manifesto carries the same project-context summary
the README does.
In #3859 I bulk-replaced `oh-my-opencode.schema.json` ->
`oh-my-openagent.schema.json` across docs/, on the false assumption
that the schema file had been renamed alongside the package.
It hadn't. Repo state on dev:
- assets/oh-my-opencode.schema.json (only file present)
- package.json export `./schema.json` -> `./dist/oh-my-opencode.schema.json`
- script/build-schema.ts emits the same legacy basename
- root AGENTS.md line 135 documents the same legacy URL
So every doc that pointed at `oh-my-openagent.schema.json` was
returning HTTP 404 for any user copying the example into their config.
Verified live:
https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-openagent.schema.json -> 404
https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json -> 200
Files reverted to canonical legacy URL:
- docs/examples/{coding,default,planning}-focused.jsonc
- docs/guide/{overview,installation,agent-model-matching}.md
- docs/reference/configuration.md (2 occurrences)
The /docs deploy from #3860 still returned HTTP 500 with
`EvalError: Code generation from strings disallowed for this context`
(captured via `wrangler tail`).
`next-mdx-remote/rsc` compiles MDX to JSX *at runtime* using
`new Function()` style code generation. Cloudflare Workers' security
sandbox bans all dynamic code generation from strings, even from inside
trusted code, so any worker invocation that touched the docs page
threw immediately.
Switch to a build-time markdown -> HTML pipeline:
- Drop `next-mdx-remote` and `gray-matter`. Add `marked` (pure-JS,
no eval).
- `web/scripts/generate-docs-content.mjs` now runs each markdown
source through `marked.parse()` (gfm enabled) at build time and
writes the resulting HTML strings into
`web/lib/docs-content.generated.ts`.
- `web/app/[locale]/docs/page.tsx` renders each section as
`<article className="docs-content" dangerouslySetInnerHTML={{ __html: section.html }} />`.
No MDX runtime, no JSX compilation at request time, just static HTML
injection.
- `web/app/globals.css` adds a `@layer components` block targeting
`.docs-content h1..h4, p, a, ul, ol, li, blockquote, code, pre, table,
thead, th, td, hr, strong`. Same shadcn-themed look that the dropped
`mdx-components.tsx` provided, applied via CSS instead of React
component overrides.
- `web/components/docs/mdx-components.tsx` removed.
We lose MDX features (JSX inside markdown), but the docs are pure
markdown anyway. `docs/` remains the SoT; the marketing site renders
identical content with no eval and no fs at runtime.
Atlas was delegating tasks one-by-one because the workflow framed parallel
as a conditional ("if tasks can run in parallel..."), letting models default
to the safer sequential path. The new shared ATLAS_PARALLEL_BY_DEFAULT block
flips the default: parallel is mandatory; sequential requires a NAMED
blocking dependency (input dependency or file conflict).
Adds two new prompt variants — kimi (K2.6 thinking-mode calibration:
commitment framing + concrete budgets) and opus-4-7 (counters 4.7's lower
default subagent count and literal-following bias). Recalibrates default
(Claude 4.6 family), gpt (GPT-5.5 outcome-first / decision rules over
absolutes), and gemini (preserves TOOL_CALL_MANDATE; replaces stale
session_id with task_id). All five variants share the parallel mandate
positioned BEFORE the workflow so "mandate above" references resolve.
Strengthens the orchestrator-direct-edit reminder hooks
(ORCHESTRATOR_DELEGATION_REQUIRED + DIRECT_WORK_REMINDER) with the central
challenge "Do you ACTUALLY need to be the one doing this?" — replacing the
previous bullet-heavy framing.
Tests now parametrized over all 5 variants. Adds prompt-routing.test
covering GPT/Gemini/Kimi/Opus 4.7/default routing and edge cases, plus a
session_id rejection test (every variant must use task_id for retries).
The first build of #3860 failed at type-check because the generated
`lib/docs-content.generated.ts` is gitignored (regenerated on every
build) and CI's `type-check` step runs before `build`. Two fixes:
- web-ci.yml: explicit `Generate docs content from repo-root docs/`
step right after `bun install` so format-check, lint, and type-check
all see the file.
- web/package.json: add `prepare` lifecycle script. `bun install`
invokes it automatically, so a fresh local checkout boots into a
working state too.
Build still re-runs the generator via prebuild, so docs/ edits land in
the bundle without an explicit dev action.
The worker deploy from #3859 returned HTTP 500 on /docs with
`Error: [unenv] fs.readFile is not implemented yet!` (captured via
`wrangler tail`). `loadDocSource` was calling `node:fs/promises`
`readFile` inside an RSC; even though the page is generated as SSG
(`●`), Cloudflare Workers' unenv shim does not implement filesystem
reads, so any code path that reaches the worker (cache miss, prerender
fallback) fails.
Move the read to a prebuild step that emits a TypeScript module:
- `web/scripts/generate-docs-content.mjs` reads each section's source
from `<repo-root>/docs/` and writes
`web/lib/docs-content.generated.ts` containing
`export const DOC_SOURCES: Record<string, string>`.
- `web/scripts/prepare-build.mjs` invokes the generator after the
cache prune, so every `bun run build` and `bunx opennextjs-cloudflare
build` regenerates the constant module from the live `docs/`.
- `web/lib/docs-source.ts` now reads `DOC_SOURCES[file]` synchronously
with no Node I/O.
- `web/app/[locale]/docs/page.tsx` drops the `Promise.all` since reads
are synchronous.
- `web/.gitignore` excludes the generated file (kept generated, not
source-of-truth).
Effect: the bundle ships every doc as a string literal. The worker has
no `fs.readFile` call to fail. `docs/` remains the only place an
editor needs to touch.
Hephaestus's fallbackChain in src/shared/model-requirements.ts has a
single entry: gpt-5.5. The previous wording said "GPT-5.4/5.5" which
implied either was acceptable; only gpt-5.5 is. Note OpenAI API access
as an alternative to ChatGPT Plus/Pro for clarity.
Now that docs/ is the single source of truth that the marketing site
renders at build time, edits to those files must run web CI and trigger
the Cloudflare deploy. Add `docs/**` to the paths filter in both
web-ci.yml and web-deploy.yml.
A markdown-only fix in docs/ is now sufficient to redeploy
oh-my-openagent.com — no companion web/ change needed.
Replace the bespoke 16-section /docs page that pulled prose from
`messages/{locale}.json` with a build-time MDX renderer that reads the
canonical markdown in repo-root `docs/`. Each markdown file becomes
one section of the docs page, scrolled-to via the existing DocsShell
sidebar. Section data structure stays in `lib/docs-sections.ts` so the
sidebar / scroll-spy keeps working with no client changes.
Why this layout:
- One source of truth: `docs/guide/*.md`, `docs/reference/*.md`,
`docs/manifesto.md`. Edits land in one place; the website redeploys
pick them up automatically via the existing web-deploy workflow.
- Build-time only: `MDXRemote` is rendered inside an RSC and the page
is statically generated (`●` SSG). Cloudflare Workers serves the
rendered HTML; no MDX compiler runs at request time.
- next-intl unchanged for everything else: only the docs prose moves
out. `mobileHeader` and `searchPlaceholder` strings stay in
`messages/{locale}.json`; the 18 stale section keys are removed.
Files:
- web/lib/docs-sections.ts: 9 sections matching docs/ files, typed
`DocSection` with `{ id, title, file }`.
- web/lib/docs-source.ts: `loadDocSource(file)` reads
`<repo-root>/docs/<file>` at build time via `node:fs/promises`.
- web/components/docs/mdx-components.tsx: shadcn-styled overrides for
every markdown element (h1-h4, p, a, ul/ol/li, blockquote, code, pre,
table, hr, strong) so the rendered output matches the rest of the
site.
- web/app/[locale]/docs/page.tsx: rewritten as an async RSC that loads
every section's source in parallel and renders one MDXRemote per
section inside DocsShell.
- web/messages/{en,ja,ko,zh}.json: `docs` key trimmed from 20 entries
to 2 (mobileHeader, searchPlaceholder).
- web/package.json: + next-mdx-remote, + gray-matter.
Local verification: `bun run format:check`, `bun run lint`,
`bun run type-check`, `bun run build`, `bunx opennextjs-cloudflare
build` all pass; `/[locale]/docs` builds as static for all 4 locales
at 4.12 kB / 132 kB First Load.