diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1c11f8b38..80ca69fd9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -33,12 +33,20 @@ jobs:
fi
test:
- runs-on: ubuntu-latest
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "24"
+
- name: Build lsp-tools-mcp submodule
run: npm ci && npm run build
working-directory: packages/lsp-tools-mcp
@@ -61,12 +69,20 @@ jobs:
run: bun test
typecheck:
- runs-on: ubuntu-latest
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "24"
+
- name: Build lsp-tools-mcp submodule
run: npm ci && npm run build
working-directory: packages/lsp-tools-mcp
@@ -91,8 +107,12 @@ jobs:
- name: Type check script tooling
run: bun run typecheck:script
- windows-codex:
- runs-on: windows-latest
+ codex-compatibility:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
with:
@@ -116,12 +136,12 @@ jobs:
env:
BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/cli @ast-grep/napi"
- - name: Run Codex Windows compatibility tests
- run: bun run test:windows-codex
+ - name: Run Codex compatibility tests
+ run: bun run test:codex
build:
runs-on: ubuntu-latest
- needs: [test, typecheck, windows-codex]
+ needs: [test, typecheck, codex-compatibility]
permissions:
contents: write
steps:
diff --git a/package.json b/package.json
index d7c6176ed..1d28e42d6 100644
--- a/package.json
+++ b/package.json
@@ -63,7 +63,8 @@
"typecheck:packages": "tsgo --noEmit -p packages/rules-engine/tsconfig.json && tsgo --noEmit -p packages/ast-grep-core/tsconfig.json && tsgo --noEmit -p packages/ast-grep-mcp/tsconfig.json && tsgo --noEmit -p packages/utils/tsconfig.json && tsgo --noEmit -p packages/model-core/tsconfig.json && tsgo --noEmit -p packages/prompts-core/tsconfig.json && tsgo --noEmit -p packages/comment-checker-core/tsconfig.json && tsgo --noEmit -p packages/hashline-core/tsconfig.json && tsgo --noEmit -p packages/boulder-state/tsconfig.json && tsgo --noEmit -p packages/agents-md-core/tsconfig.json && tsgo --noEmit -p packages/omo-codex/tsconfig.json",
"typecheck:script": "tsgo --noEmit -p script/tsconfig.json",
"test": "bun test",
- "test:windows-codex": "bun test src/cli/install-codex/codex-cache.test.ts src/cli/install-codex/install-codex.test.ts packages/omo-codex/src/**/*.test.ts packages/utils/src/jsonc-parser.test.ts packages/utils/src/frontmatter.test.ts packages/hashline-core/src/hash-computation.test.ts packages/hashline-core/src/smoke-untested-modules.test.ts packages/rules-engine/src/index.test.ts packages/rules-engine/src/security-boundary.test.ts packages/agents-md-core/src/injector.test.ts && node --test packages/omo-codex/plugin/test/*.test.mjs packages/omo-codex/scripts/install-local.test.mjs",
+ "test:codex": "bun test src/cli/install-codex/codex-cache.test.ts src/cli/install-codex/install-codex.test.ts packages/omo-codex/src/**/*.test.ts packages/utils/src/jsonc-parser.test.ts packages/utils/src/frontmatter.test.ts packages/hashline-core/src/hash-computation.test.ts packages/hashline-core/src/smoke-untested-modules.test.ts packages/rules-engine/src/index.test.ts packages/rules-engine/src/security-boundary.test.ts packages/agents-md-core/src/injector.test.ts && node --test packages/omo-codex/plugin/test/*.test.mjs packages/omo-codex/scripts/install-local.test.mjs",
+ "test:windows-codex": "bun run test:codex",
"build:ast-grep-mcp": "bun run --cwd packages/ast-grep-mcp build"
},
"keywords": [
diff --git a/script/package-layout.test.ts b/script/package-layout.test.ts
index a4f3c18fd..d4a75388a 100644
--- a/script/package-layout.test.ts
+++ b/script/package-layout.test.ts
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url"
const repositoryRoot = fileURLToPath(new URL("..", import.meta.url))
const commandRoots = [".opencode/command", ".agents/command"] as const
const skillRoots = [".opencode/skills", ".agents/skills"] as const
+const packDryRunTimeoutMs = 15_000
class PackDryRunError extends Error {
constructor(readonly exitCode: number, readonly stderr: string) {
@@ -130,5 +131,5 @@ describe("published package layout", () => {
// then
const missingPaths = expectedAssetPaths.filter((expectedPath) => !packedPaths.has(expectedPath))
expect(missingPaths).toEqual([])
- })
+ }, packDryRunTimeoutMs)
})
diff --git a/script/publish-workflow.test.ts b/script/publish-workflow.test.ts
index ef2f3b070..2059dbce1 100644
--- a/script/publish-workflow.test.ts
+++ b/script/publish-workflow.test.ts
@@ -3,9 +3,11 @@
import { describe, expect, test } from "bun:test"
import { readFileSync } from "node:fs"
+const ciWorkflowPath = new URL("../.github/workflows/ci.yml", import.meta.url)
+
const workflowChecks = [
{
- path: new URL("../.github/workflows/ci.yml", import.meta.url),
+ path: ciWorkflowPath,
testRuns: [
"run: bun test",
"run: bun test src/shared/dist-bundle-bun-globals.test.ts",
@@ -28,4 +30,32 @@ describe("test workflows", () => {
}
}
})
+
+ test("exercise root checks across linux macos and windows", () => {
+ // #given
+ const workflow = readFileSync(ciWorkflowPath, "utf8")
+
+ // #when
+ const hasCrossOsMatrix = workflow.includes("os: [ubuntu-latest, macos-latest, windows-latest]")
+ const hasMatrixRunner = workflow.includes("runs-on: ${{ matrix.os }}")
+
+ // #then
+ expect(hasCrossOsMatrix, "CI root checks must cover Linux, macOS, and Windows").toBe(true)
+ expect(hasMatrixRunner, "CI root checks must run on the selected matrix OS").toBe(true)
+ })
+
+ test("runs codex compatibility checks on every supported os", () => {
+ // #given
+ const workflow = readFileSync(ciWorkflowPath, "utf8")
+
+ // #when
+ const hasCodexMatrixJob = workflow.includes("codex-compatibility:")
+ const hasCodexCommand = workflow.includes("run: bun run test:codex")
+ const buildNeedsCodexMatrix = workflow.includes("needs: [test, typecheck, codex-compatibility]")
+
+ // #then
+ expect(hasCodexMatrixJob, "CI must expose a Codex compatibility matrix job").toBe(true)
+ expect(hasCodexCommand, "Codex compatibility job must run the shared Codex test script").toBe(true)
+ expect(buildNeedsCodexMatrix, "Build must wait for Codex compatibility checks").toBe(true)
+ })
})
diff --git a/src/cli/install-codex/install-codex.test.ts b/src/cli/install-codex/install-codex.test.ts
index 465f72a0d..075c359bc 100644
--- a/src/cli/install-codex/install-codex.test.ts
+++ b/src/cli/install-codex/install-codex.test.ts
@@ -1,3 +1,6 @@
+///
+///
+
import { describe, expect, test } from "bun:test"
import { mkdtemp, readFile, stat } from "node:fs/promises"
import { tmpdir } from "node:os"
@@ -9,7 +12,7 @@ describe("install-codex", () => {
// given
const codexHome = await mkdtemp(join(tmpdir(), "omo-codex-home-"))
const binDir = await mkdtemp(join(tmpdir(), "omo-codex-bin-"))
- const repoRoot = "/Users/yeongyu/local-workspaces/omodex"
+ const repoRoot = process.cwd()
// when
const first = await runCodexInstaller({ codexHome, binDir, repoRoot, runCommand: async () => undefined })