diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..167fb96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Third-party reference source (working-copy only, see AirPlan DD §23) — not committed +/reference/ diff --git a/reference/air-suite-20260518/.gitkeep b/reference/air-suite-20260518/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/reference/anthropic-skills/.claude-plugin/marketplace.json b/reference/anthropic-skills/.claude-plugin/marketplace.json deleted file mode 100644 index 03d8e71..0000000 --- a/reference/anthropic-skills/.claude-plugin/marketplace.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "anthropic-agent-skills", - "owner": { - "name": "Keith Lazuka", - "email": "klazuka@anthropic.com" - }, - "metadata": { - "description": "Anthropic example skills", - "version": "1.0.0" - }, - "plugins": [ - { - "name": "document-skills", - "description": "Collection of document processing suite including Excel, Word, PowerPoint, and PDF capabilities", - "source": "./", - "strict": false, - "skills": [ - "./skills/xlsx", - "./skills/docx", - "./skills/pptx", - "./skills/pdf" - ] - }, - { - "name": "example-skills", - "description": "Collection of example skills demonstrating various capabilities including skill creation, MCP building, visual design, algorithmic art, internal communications, web testing, artifact building, Slack GIFs, and theme styling", - "source": "./", - "strict": false, - "skills": [ - "./skills/algorithmic-art", - "./skills/brand-guidelines", - "./skills/canvas-design", - "./skills/doc-coauthoring", - "./skills/frontend-design", - "./skills/internal-comms", - "./skills/mcp-builder", - "./skills/skill-creator", - "./skills/slack-gif-creator", - "./skills/theme-factory", - "./skills/web-artifacts-builder", - "./skills/webapp-testing" - ] - } - , - { - "name": "claude-api", - "description": "Claude API and SDK documentation skill for building LLM-powered applications", - "source": "./", - "strict": false, - "skills": [ - "./skills/claude-api" - ] - } - ] -} diff --git a/reference/anthropic-skills/.gitignore b/reference/anthropic-skills/.gitignore deleted file mode 100644 index 4ff6017..0000000 --- a/reference/anthropic-skills/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.DS_Store -__pycache__/ -.idea/ -.vscode/ - diff --git a/reference/anthropic-skills/.gitkeep b/reference/anthropic-skills/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/reference/asciinema-3.2.0/.cargo/config.toml b/reference/asciinema-3.2.0/.cargo/config.toml deleted file mode 100755 index 1309df4..0000000 --- a/reference/asciinema-3.2.0/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[env] -RUST_TEST_THREADS = "1" diff --git a/reference/asciinema-3.2.0/.gitattributes b/reference/asciinema-3.2.0/.gitattributes deleted file mode 100755 index fbbfa57..0000000 --- a/reference/asciinema-3.2.0/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -assets/asciinema-player.* linguist-vendored diff --git a/reference/asciinema-3.2.0/.github/ISSUE_TEMPLATE/bug-report.yml b/reference/asciinema-3.2.0/.github/ISSUE_TEMPLATE/bug-report.yml deleted file mode 100755 index 2a04093..0000000 --- a/reference/asciinema-3.2.0/.github/ISSUE_TEMPLATE/bug-report.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: Bug Report -description: Report a bug to help improve asciinema CLI -body: - - type: markdown - attributes: - value: | - **This is a bug tracker for asciinema CLI (the recorder).** - - - If your issue is with the JavaScript player or server, please open an issue in the related repository - - If you're experiencing issues with asciinema.org, contact admin@asciinema.org - - For feature requests, questions, and discussions, use the [forum](https://discourse.asciinema.org) or [GitHub discussions](https://github.com/orgs/asciinema/discussions) - - Thanks for taking the time to report a bug! Please fill out the sections below. - - - type: checkboxes - id: checks - attributes: - label: Pre-submission checks - description: Please confirm the following before submitting your bug report - options: - - label: I have searched existing issues and this bug has not been reported yet - required: true - - label: This is a bug report for asciinema CLI (not player or server) - required: true - - - type: textarea - id: bug-description - attributes: - label: Bug Description - description: A clear and concise description of what the bug is. - placeholder: Describe the bug... - validations: - required: true - - - type: textarea - id: reproduction-steps - attributes: - label: Steps to Reproduce - description: Provide detailed steps to reproduce the behavior - placeholder: | - 1. Run command `asciinema ...` - 2. Do action '...' - 3. Observe error - validations: - required: true - - - type: textarea - id: expected-behavior - attributes: - label: Expected Behavior - description: A clear and concise description of what you expected to happen. - placeholder: What should have happened instead? - validations: - required: true - - - type: input - id: os-version - attributes: - label: Operating System - description: Which OS and version are you using? - placeholder: e.g., Ubuntu 24.04, macOS 14.0, Fedora 39 - validations: - required: true - - - type: input - id: cli-version - attributes: - label: asciinema CLI Version - description: What version of asciinema CLI are you using? Run `asciinema --version` to check. - placeholder: e.g., 2.4.0 - validations: - required: true - - - type: dropdown - id: installation-method - attributes: - label: Installation Method - description: How did you install asciinema CLI? - options: - - Package manager (apt, yum, brew, etc.) - - pip/pipx - - Built from source - - Downloaded binary - - Other - validations: - required: true - - - type: textarea - id: terminal-info - attributes: - label: Terminal Information - description: What terminal emulator and shell are you using? - placeholder: | - Terminal: e.g., GNOME Terminal, iTerm2, Ghostty - Shell: e.g., bash 5.1, zsh 5.8, fish 3.6 - - - type: textarea - id: additional-context - attributes: - label: Additional Context - description: Add any other context, screenshots, or relevant information about the problem here. diff --git a/reference/asciinema-3.2.0/.github/ISSUE_TEMPLATE/config.yml b/reference/asciinema-3.2.0/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100755 index 10beddd..0000000 --- a/reference/asciinema-3.2.0/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Forum - url: https://discourse.asciinema.org/ - about: Ideas, feature requests, help requests, questions and general discussions should be posted here. - - name: GitHub discussions - url: https://github.com/orgs/asciinema/discussions - about: Ideas, feature requests, help requests, questions and general discussions should be posted here. diff --git a/reference/asciinema-3.2.0/.github/workflows/ci.yml b/reference/asciinema-3.2.0/.github/workflows/ci.yml deleted file mode 100755 index bbafd6c..0000000 --- a/reference/asciinema-3.2.0/.github/workflows/ci.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: CI - -on: - push: - branches: ["develop"] - pull_request: - branches: ["develop"] - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest] - rust: [default, msrv] - - steps: - - uses: actions/checkout@v5 - - - name: Install Nix - uses: nixbuild/nix-quick-install-action@v34 - - - name: Setup Nix cache - uses: nix-community/cache-nix-action@v6 - with: - primary-key: nix-${{ runner.os }}-${{ matrix.rust }}-${{ hashFiles('**/*.nix', '**/flake.lock') }} - restore-prefixes-first-match: nix-${{ runner.os }}-${{ matrix.rust }}- - - - name: Build - run: nix develop .#${{ matrix.rust }} --command cargo build --verbose - - - name: Run cargo tests - run: nix develop .#${{ matrix.rust }} --command cargo test --verbose - - - name: Run integration tests - run: nix develop .#${{ matrix.rust }} --command tests/integration.sh - - - name: Check formatting - run: nix develop .#${{ matrix.rust }} --command cargo fmt --check - - - name: Lint with clippy - run: nix develop .#${{ matrix.rust }} --command cargo clippy diff --git a/reference/asciinema-3.2.0/.github/workflows/release.yml b/reference/asciinema-3.2.0/.github/workflows/release.yml deleted file mode 100755 index af2b04a..0000000 --- a/reference/asciinema-3.2.0/.github/workflows/release.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Release - -permissions: - contents: write - -on: - push: - tags: - - v[0-9]+.* - -jobs: - create-release: - name: Create GH release draft - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v5 - - - name: Create the release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh release create ${{ github.ref_name }} --draft --verify-tag --title ${{ github.ref_name }} - - upload-binary: - needs: create-release - name: ${{ matrix.target }} - runs-on: ${{ matrix.os }} - - strategy: - matrix: - include: - - os: ubuntu-latest - target: x86_64-unknown-linux-gnu - use-cross: false - - - os: ubuntu-latest - target: x86_64-unknown-linux-musl - use-cross: false - - - os: ubuntu-latest - target: aarch64-unknown-linux-gnu - use-cross: true - - - os: macos-latest - target: x86_64-apple-darwin - use-cross: false - - - os: macos-latest - target: aarch64-apple-darwin - use-cross: false - - env: - CARGO: cargo - - steps: - - uses: actions/checkout@v5 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ matrix.target }} - - - name: Install cross - if: matrix.use-cross - uses: taiki-e/install-action@v2 - with: - tool: cross - - - name: Overwrite build command env variable - if: matrix.use-cross - shell: bash - run: echo "CARGO=cross" >> $GITHUB_ENV - - - name: Install build deps - shell: bash - run: | - if [[ ${{ matrix.target }} == x86_64-unknown-linux-musl ]]; then - sudo apt-get update - sudo apt-get install -y musl-tools - fi - - - name: Build release binary - run: ${{ env.CARGO }} build --release --locked --target ${{ matrix.target }} - - - name: Upload the binary to the release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - mv target/${{ matrix.target }}/release/asciinema target/release/asciinema-${{ matrix.target }} - gh release upload ${{ github.ref_name }} target/release/asciinema-${{ matrix.target }} diff --git a/reference/asciinema-3.2.0/.gitignore b/reference/asciinema-3.2.0/.gitignore deleted file mode 100755 index 500c22a..0000000 --- a/reference/asciinema-3.2.0/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -target/ -.envrc -.direnv -/result diff --git a/reference/asciinema-3.2.0/.gitkeep b/reference/asciinema-3.2.0/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/reference/atuin-18.16.1/.atuin/skills/release/SKILL.md b/reference/atuin-18.16.1/.atuin/skills/release/SKILL.md deleted file mode 100755 index cace07b..0000000 --- a/reference/atuin-18.16.1/.atuin/skills/release/SKILL.md +++ /dev/null @@ -1,213 +0,0 @@ ---- -name: release -description: > - Orchestrate a multi-step Atuin CLI release — version bumping, changelog - generation, PR creation, tagging, and crates.io publishing. Invoke with - /release or /release . -disable-model-invocation: true -argument-hint: [version] ---- - -# Atuin CLI Release - -You are orchestrating a release of the Atuin CLI. Follow the steps below -**in order**, pausing at each checkpoint for user confirmation. Do not skip -steps or combine them. - -## Current State - -- Workspace version: !`sed -n '/^\[workspace\.package\]/,/^\[/s/^version = "\(.*\)"/\1/p' Cargo.toml` -- Latest tag: !`git describe --tags --abbrev=0 2>/dev/null || echo "none"` -- Suggested next version: !`git-cliff --bumped-version 2>/dev/null | sed 's/^v//' || echo "(unknown)"` - ---- - -## Step 1 — Check Dependencies - -Verify these tools are installed: `git`, `gsed`, `cargo`, `gh`, `git-cliff`. - -Use `command -v` for each. If any are missing, report which ones and stop. - -Remember to use `gsed`, or else macOS flags to regular `sed`, later in the workflow. - ---- - -## Step 2 — Determine Version - -The target version may be provided as `$ARGUMENTS`. If it's empty, use -AskUserQuestion to ask for the new version (show the current state above -for reference). - -After determining the version: -- If it contains a `-` (e.g. `18.15.0-beta.1`), it is a **prerelease**. - Note this — it affects changelog and publish behavior later. -- Show the user: `current → new` and whether it's a prerelease. -- **Checkpoint:** Ask the user to confirm before proceeding. - ---- - -## Step 3 — Set Up Working Directory - -Clone a fresh copy into a temp directory: - -```bash -WORKDIR=$(mktemp -d) -git clone git@github.com:atuinsh/atuin.git "$WORKDIR" -``` - -Print the working directory path so the user can find it if needed. - -NOTE: -ALL subsequent Bash commands run from `$WORKDIR`. - ---- - -## Step 4 — Create Branch & Update Versions - -1. Create a release branch named after the version (no `v` prefix): - `git checkout -b ` - -2. Replace the old version with the new one in all `Cargo.toml` files. - **Escape dots** in the old version so sed treats them literally: - - ```bash - VERSION_PATTERN="${OLD_VERSION//./\\.}" - find . -type f -name 'Cargo.toml' -not -path './.git/*' \ - -exec gsed -i "s/$VERSION_PATTERN/$NEW_VERSION/g" {} \; - ``` - -3. Run `cargo check` to update `Cargo.lock`. - -4. Show `git diff --stat` and the version-related lines from the diff: - ```bash - git diff --unified=0 -- '*.toml' | grep '^\+.*version' | grep -vF '+++' - ``` - Remember to use macOS grep arguments on macOS systems. - -5. Verify the workspace version was actually updated by re-reading it - from `Cargo.toml`. - -6. **Checkpoint:** Show the diff summary and ask the user to confirm the - version changes look correct. - ---- - -## Step 5 — Update Changelog - -The changelog strategy differs for prereleases vs stable releases: - -- **Prerelease:** Maintain a running `## [unreleased]` section containing - all changes since the last stable release. Use: - `git-cliff --unreleased --strip all` - (cliff.toml's `ignore_tags` already ignores beta/alpha tags, so - `--unreleased` spans back to the last stable release automatically.) - -- **Stable release:** Generate a versioned entry that replaces the - `[unreleased]` section. Use: - `git-cliff --unreleased --tag "v" --strip all` - -Then update `CHANGELOG.md`: - -1. If an existing `## [unreleased]` or `## [Unreleased]` section exists, - **remove it entirely** (the heading and all content up to the next - `## ` heading). - -2. Insert the new entry before the first existing `## ` version heading. - -3. **Checkpoint:** Read and display the new changelog entry to the user. - Ask if they want any edits. If so, make the requested changes using - the Edit tool. Repeat until they're satisfied. - ---- - -## Step 6 — Commit & Push - -Stage all changes and commit: - -``` -chore(release): prepare for release -``` - -Push the branch with `--set-upstream origin`. - ---- - -## Step 7 — Create PR & Wait for Merge - -### Create the PR - -Extract the changelog entry body (everything between the new `## ` heading -and the next one) for the PR description. - -For prereleases, the heading to match is `## [unreleased]`. -For stable releases, it's `## ` (escape dots in the awk pattern). - -Create the PR: -```bash -gh pr create \ - --title "chore(release): prepare for release " \ - --body "" \ - --repo atuinsh/atuin - --draft -``` - -Show the PR URL to the user. Tell the user to go review and merge the PR. - -When the user reports the PR is merged, proceed to the next step. - ---- - -## Step 8 — Tag Release - -Back in the working directory: - -```bash -git checkout main -git pull -git tag "v" -git push --tags -``` - -Tell the user the tag was pushed and the release CI workflow has been -triggered. - ---- - -## Step 9 — Publish to crates.io - -**If this is a prerelease**, skip this step entirely and tell the user. - -**If this is a stable release**, ask the user whether to publish. - -If yes, publish each crate **in dependency order** using `--no-verify` -(the code already passed CI, and verification fails when crates.io -hasn't indexed a freshly-published dependency yet): - -``` -atuin-common, atuin-client, atuin-ai, atuin-dotfiles, atuin-history, -atuin-nucleo/matcher, atuin-nucleo, atuin-daemon, atuin-kv, -atuin-scripts, atuin-server-database, atuin-server-postgres, -atuin-server-sqlite, atuin-server, atuin-pty-proxy, atuin -``` - -For each crate, run from `crates/`: -```bash -cargo publish --no-verify 2>&1 -``` - -If it fails with "already uploaded", report it as a skip (not an error) — -some crates like `atuin-nucleo` are versioned independently and may -already be published at their current version. - -If it fails for any other reason, stop and report the error. - ---- - -## Completion - -Summarize what was done: -- Version released -- PR URL -- Tag name -- Which crates were published (if any) -- Working directory path and how to clean it up (`rm -rf`) diff --git a/reference/atuin-18.16.1/.cargo/audit.toml b/reference/atuin-18.16.1/.cargo/audit.toml deleted file mode 100755 index 11cef85..0000000 --- a/reference/atuin-18.16.1/.cargo/audit.toml +++ /dev/null @@ -1,9 +0,0 @@ -[advisories] -ignore = [ - # This is a vuln on RSA. RSA is in our lockfile, but not in cargo-tree. - # It is a issue with sqlx/cargo, and does not affect Atuin. - # See: - # - https://github.com/launchbadge/sqlx/issues/3211 - # - https://github.com/rust-lang/cargo/issues/10801 - "RUSTSEC-2023-0071" -] diff --git a/reference/atuin-18.16.1/.claude/skills/hunk/SKILL.md b/reference/atuin-18.16.1/.claude/skills/hunk/SKILL.md deleted file mode 100755 index 6670322..0000000 --- a/reference/atuin-18.16.1/.claude/skills/hunk/SKILL.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -name: hunk-review -description: Interacts with live Hunk diff review sessions via CLI. Inspects review focus, navigates files and hunks, reloads session contents, and adds inline review comments. Use when the user has a Hunk session running or wants to review diffs interactively. ---- - -# Hunk Review - -Hunk is an interactive terminal diff viewer. The TUI is for the user -- do NOT run `hunk diff`, `hunk show`, or other interactive commands directly. Use `hunk session *` CLI commands to inspect and control live sessions through the local daemon. - -If no session exists, ask the user to launch Hunk in their terminal first. - -## Workflow - -```text -1. hunk session list # find live sessions -2. hunk session get --repo . # inspect path / repo / source -3. hunk session review --repo . --json # inspect file/hunk structure first -4. hunk session review --repo . --include-patch --json # opt into raw diff text only when needed -5. hunk session context --repo . # check current focus when needed -6. hunk session navigate ... # move to the right place -7. hunk session reload -- # swap contents if needed -8. hunk session comment add ... # leave one review note -9. hunk session comment apply ... # apply many agent notes in one stdin batch -``` - -## Session selection - -Most session commands accept: - -- `--repo ` -- match the live session by its current loaded repo root (most common) -- `` -- match by exact ID (use when multiple sessions share a repo) -- If only one session exists, it auto-resolves - -`reload` also supports: - -- `--session-path ` -- match the live Hunk window by its current working directory -- `--source ` -- load the replacement `diff` / `show` command from a different directory - -Use `--source` only for advanced reloads where the live session you want to control is not already associated with the checkout you want to load next. For a normal worktree session, prefer selecting it directly with `--repo /path/to/worktree`. - -## Commands - -### Inspect - -```bash -hunk session list [--json] -hunk session get (--repo . | ) [--json] -hunk session context (--repo . | ) [--json] -hunk session review (--repo . | ) [--json] [--include-patch] -``` - -- `get` shows the session `Path`, `Repo`, and `Source`, which helps when choosing between `--repo` and `--session-path` -- `Repo` is what `--repo` matches; `Path` is what `--session-path` matches -- `review --json` returns file and hunk structure by default; add `--include-patch` only when a caller truly needs raw unified diff text - -### Navigate - -Absolute navigation requires `--file` and exactly one of `--hunk`, `--new-line`, or `--old-line`: - -```bash -hunk session navigate --repo . --file src/App.tsx --hunk 2 -hunk session navigate --repo . --file src/App.tsx --new-line 372 -hunk session navigate --repo . --file src/App.tsx --old-line 355 -``` - -Relative comment navigation jumps between annotated hunks and does not require `--file`: - -```bash -hunk session navigate --repo . --next-comment -hunk session navigate --repo . --prev-comment -``` - -- `--hunk ` is 1-based -- `--new-line` / `--old-line` are 1-based line numbers on that diff side -- Use either `--next-comment` or `--prev-comment`, not both - -### Reload - -Swaps the live session's contents. Pass a Hunk review command after `--`: - -```bash -hunk session reload --repo . -- diff -hunk session reload --repo . -- diff main...feature -- src/ui -hunk session reload --repo . -- show HEAD~1 -hunk session reload --repo . -- show HEAD~1 -- README.md -hunk session reload --repo /path/to/worktree -- diff -hunk session reload --session-path /path/to/live-window --source /path/to/other-checkout -- diff -``` - -- Always include `--` before the nested Hunk command -- `--repo` or `` usually selects the session you want -- `--source` is advanced: it does not select the session; it only changes where the replacement review command runs -- If the live session is already showing the target worktree, prefer `hunk session reload --repo /path/to/worktree -- diff` -- `--session-path` targets the live window when you need to keep session selection separate from reload source - -### Comments - -```bash -hunk session comment add --repo . --file README.md --new-line 103 --summary "Tighten this wording" [--rationale "..."] [--author "agent"] [--focus] -printf '%s\n' '{"comments":[{"filePath":"README.md","newLine":103,"summary":"Tighten this wording"}]}' | hunk session comment apply --repo . --stdin [--focus] -hunk session comment list --repo . [--file README.md] -hunk session comment rm --repo . -hunk session comment clear --repo . --yes [--file README.md] -``` - -- `comment add` is best for one note; `comment apply` is best when an agent already has several notes ready -- `comment add` requires `--file`, `--summary`, and exactly one of `--old-line` or `--new-line` -- `comment apply` payload items require `filePath`, `summary`, and exactly one target such as `hunk`, `hunkNumber`, `oldLine`, or `newLine` -- `comment apply` reads a JSON batch from stdin and validates the full batch before mutating the live session -- Pass `--focus` when you want to jump to the new note or the first note in a batch -- `comment list` and `comment clear` accept optional `--file` -- Quote `--summary` and `--rationale` defensively in the shell - -## New files in working-tree reviews - -`hunk diff` includes untracked files by default. If the user wants tracked changes only, reload with `--exclude-untracked`: - -```bash -hunk session reload --repo . -- diff --exclude-untracked -``` - -## Guiding a review - -The user may ask you to walk them through a changeset or review code using Hunk. Start with `hunk session review --json` to understand the file/hunk structure without inflating agent context, then use `--include-patch` only for the files you truly need to read in raw diff form. Use `context` and `navigate` to line up the user's current view before adding comments. - -Your role is to narrate: steer the user's view to what matters and leave comments that explain what they're looking at. - -Typical flow: - -1. Load the right content (`reload` if needed) -2. Navigate to the first interesting file / hunk -3. Add a comment explaining what's happening and why -4. If you already have several notes ready, prefer one `comment apply` batch over many separate shell invocations -5. Summarize when done - -Guidelines: - -- Work in the order that tells the clearest story, not necessarily file order -- Navigate before commenting so the user sees the code you're discussing -- Use `comment apply` for agent-generated batches and `comment add` for one-off notes -- Use `--focus` sparingly when the note itself should actively steer the review -- Keep comments focused: intent, structure, risks, or follow-ups -- Don't comment on every hunk -- highlight what the user wouldn't spot themselves - -## Common errors - -- **"No visible diff file matches ..."** -- the file is not in the loaded review. Check `context`, then `reload` if needed. -- **"No active Hunk sessions"** -- ask the user to open Hunk in their terminal. -- **"Multiple active sessions match"** -- pass `` explicitly. -- **"No active Hunk session matches session path ..."** -- for advanced split-path reloads, verify the live window `Path` via `hunk session get` or `list`, then use `--session-path`. -- **"Pass the replacement Hunk command after `--`"** -- include `--` before the nested `diff` / `show` command. -- **"Pass --stdin to read batch comments from stdin JSON."** -- `comment apply` only reads its batch payload from stdin. -- **"Specify exactly one navigation target"** -- pick one of `--hunk`, `--old-line`, or `--new-line`. -- **"Specify either --next-comment or --prev-comment, not both."** -- choose one comment-navigation direction. diff --git a/reference/atuin-18.16.1/.claude/skills/release/SKILL.md b/reference/atuin-18.16.1/.claude/skills/release/SKILL.md deleted file mode 100755 index 7884923..0000000 --- a/reference/atuin-18.16.1/.claude/skills/release/SKILL.md +++ /dev/null @@ -1,269 +0,0 @@ ---- -name: release -description: > - Orchestrate a multi-step Atuin CLI release — version bumping, changelog - generation, PR creation, tagging, and crates.io publishing. Invoke with - /release or /release . -disable-model-invocation: true -argument-hint: [version] ---- - -# Atuin CLI Release - -You are orchestrating a release of the Atuin CLI. Follow the steps below -**in order**, pausing at each checkpoint for user confirmation. Do not skip -steps or combine them. - -## Current State - -- Workspace version: !`sed -n '/^\[workspace\.package\]/,/^\[/s/^version = "\(.*\)"/\1/p' Cargo.toml` -- Latest tag: !`git describe --tags --abbrev=0 2>/dev/null || echo "none"` -- Suggested next version: !`git-cliff --bumped-version 2>/dev/null | sed 's/^v//' || echo "(unknown)"` - ---- - -## Step 1 — Check Dependencies - -Verify these tools are installed: `git`, `gsed`, `cargo`, `gh`, `git-cliff`. - -Use `command -v` for each. If any are missing, report which ones and stop. - ---- - -## Step 2 — Determine Version - -The target version may be provided as `$ARGUMENTS`. If it's empty, use -AskUserQuestion to ask for the new version (show the current state above -for reference). - -After determining the version: -- If it contains a `-` (e.g. `18.15.0-beta.1`), it is a **prerelease**. - Note this — it affects changelog and publish behavior later. -- Show the user: `current → new` and whether it's a prerelease. -- **Checkpoint:** Ask the user to confirm before proceeding. - ---- - -## Step 3 — Set Up Working Directory - -Clone a fresh copy into a temp directory: - -```bash -WORKDIR=$(mktemp -d) -git clone git@github.com:atuinsh/atuin.git "$WORKDIR" -``` - -Print the working directory path so the user can find it if needed. -All subsequent Bash commands run from `$WORKDIR`. - ---- - -## Step 4 — Create Branch & Update Versions - -1. Create a release branch named after the version (no `v` prefix): - `git checkout -b ` - -2. Replace the old version with the new one in all `Cargo.toml` files. - **Escape dots** in the old version so sed treats them literally: - - ```bash - VERSION_PATTERN="${OLD_VERSION//./\\.}" - find . -type f -name 'Cargo.toml' -not -path './.git/*' \ - -exec gsed -i "s/$VERSION_PATTERN/$NEW_VERSION/g" {} \; - ``` - -3. Run `cargo check` to update `Cargo.lock`. - -4. Show `git diff --stat` and the version-related lines from the diff: - ```bash - git diff --unified=0 -- '*.toml' | grep -E '^\+.*version' | grep -v '^\+\+\+' - ``` - -5. Verify the workspace version was actually updated by re-reading it - from `Cargo.toml`. - -6. **Checkpoint:** Show the diff summary and ask the user to confirm the - version changes look correct. - ---- - -## Step 5 — Update Changelog - -The changelog strategy differs for prereleases vs stable releases: - -- **Prerelease:** Maintain a running `## [unreleased]` section containing - all changes since the last stable release. Use: - `git-cliff --unreleased --strip all` - (cliff.toml's `ignore_tags` already ignores beta/alpha tags, so - `--unreleased` spans back to the last stable release automatically.) - -- **Stable release:** Generate a versioned entry that replaces the - `[unreleased]` section. Use: - `git-cliff --unreleased --tag "v" --strip all` - -Then update `CHANGELOG.md`: - -1. If an existing `## [unreleased]` or `## [Unreleased]` section exists, - **remove it entirely** (the heading and all content up to the next - `## ` heading). - -2. Insert the new entry before the first existing `## ` version heading. - -3. **Checkpoint:** Read and display the new changelog entry to the user. - Ask if they want any edits. If so, make the requested changes using - the Edit tool. Repeat until they're satisfied. - ---- - -## Step 6 — Commit & Push - -Stage all changes and commit: - -``` -chore(release): prepare for release -``` - -Push the branch with `--set-upstream origin`. - ---- - -## Step 7 — Create PR & Wait for Merge - -### Create the PR - -Extract the changelog entry body (everything between the new `## ` heading -and the next one) for the PR description. - -For prereleases, the heading to match is `## [unreleased]`. -For stable releases, it's `## ` (escape dots in the awk pattern). - -Create the PR: -```bash -gh pr create \ - --title "chore(release): prepare for release " \ - --body "" \ - --repo atuinsh/atuin -``` - -Show the PR URL to the user. - -### Wait for merge - -Start a **persistent Monitor** that polls the PR status every 30 seconds. -The monitor script must: -- **Only emit output** on meaningful state changes: all checks green, PR - merged, or PR closed. Silent polls keep the monitor quiet and avoid - flooding notifications. -- Handle transient API errors gracefully (don't crash on a single failure) -- Exit 0 on `MERGED`, exit 1 on `CLOSED` - -The rollup mixes two entry shapes: `CheckRun` entries use `status` + -`conclusion`, while `StatusContext` entries use `state`. A check counts -as "passing" when it's in a terminal state with a non-failing outcome. -Treat `SUCCESS`, `SKIPPED`, and `NEUTRAL` as passing — some release -workflows (e.g. `announce`, `build-global-artifacts`) are conditional -and report `SKIPPED` on non-tag events, which is expected, not a -failure. - -Example monitor script (substitute the actual PR number): -```bash -checks_passed=false -while true; do - json=$(gh pr view PR_NUM --repo atuinsh/atuin --json state,statusCheckRollup 2>/dev/null) || { sleep 30; continue; } - state=$(echo "$json" | jq -r '.state') - case "$state" in - MERGED) echo "PR #PR_NUM has been merged!"; exit 0 ;; - CLOSED) echo "PR #PR_NUM was closed without merging."; exit 1 ;; - esac - # Only notify once when all checks reach a terminal passing state. - # CheckRun entries carry `status`/`conclusion`; StatusContext entries - # carry `state`. SKIPPED and NEUTRAL count as passing. - if [ "$checks_passed" = false ]; then - counts=$(echo "$json" | jq -r ' - [.statusCheckRollup[]?] as $all - | ($all | map(select( - (.status == "COMPLETED" and (.conclusion | IN("SUCCESS","SKIPPED","NEUTRAL"))) - or .state == "SUCCESS" - )) | length) as $passing - | ($all | map(select( - (.status == "COMPLETED" and (.conclusion | IN("FAILURE","TIMED_OUT","CANCELLED","ACTION_REQUIRED","STALE"))) - or (.state | IN("FAILURE","ERROR")) - )) | length) as $failing - | "\($all | length) \($passing) \($failing)" - ' 2>/dev/null) - read -r total passing failing <<<"$counts" - if [ "${failing:-0}" -gt 0 ] 2>/dev/null; then - echo "PR #PR_NUM has $failing failing check(s) — investigate before merging." - checks_passed=true # don't re-notify - elif [ "${total:-0}" -gt 0 ] 2>/dev/null && [ "$total" = "$passing" ]; then - echo "All $total checks passed on PR #PR_NUM — ready to merge!" - checks_passed=true - fi - fi - sleep 30 -done -``` - -Tell the user to go review and merge the PR. While the monitor runs, you -can respond to other questions — the monitor notifications will arrive -asynchronously. - -When the monitor reports `MERGED`, proceed to the next step. -If it reports `CLOSED`, inform the user and stop the release. - ---- - -## Step 8 — Tag Release - -Back in the working directory: - -```bash -git checkout main -git pull -git tag "v" -git push --tags -``` - -Tell the user the tag was pushed and the release CI workflow has been -triggered. - ---- - -## Step 9 — Publish to crates.io - -**If this is a prerelease**, skip this step entirely and tell the user. - -**If this is a stable release**, ask the user whether to publish. - -If yes, publish each crate **in dependency order** using `--no-verify` -(the code already passed CI, and verification fails when crates.io -hasn't indexed a freshly-published dependency yet): - -``` -atuin-common, atuin-client, atuin-ai, atuin-dotfiles, atuin-history, -atuin-nucleo/matcher, atuin-nucleo, atuin-daemon, atuin-kv, -atuin-scripts, atuin-server-database, atuin-server-postgres, -atuin-server-sqlite, atuin-server, atuin-pty-proxy, atuin -``` - -For each crate, run from `crates/`: -```bash -cargo publish --no-verify 2>&1 -``` - -If it fails with "already uploaded", report it as a skip (not an error) — -some crates like `atuin-nucleo` are versioned independently and may -already be published at their current version. - -If it fails for any other reason, stop and report the error. - ---- - -## Completion - -Summarize what was done: -- Version released -- PR URL -- Tag name -- Which crates were published (if any) -- Working directory path and how to clean it up (`rm -rf`) diff --git a/reference/atuin-18.16.1/.codespellrc b/reference/atuin-18.16.1/.codespellrc deleted file mode 100755 index bd9ada0..0000000 --- a/reference/atuin-18.16.1/.codespellrc +++ /dev/null @@ -1,7 +0,0 @@ -[codespell] -# Ref: https://github.com/codespell-project/codespell#using-a-config-file -skip = .git*,*.lock,.codespellrc,CODE_OF_CONDUCT.md,CONTRIBUTORS -check-hidden = true -# ignore-regex = -ignore-words-list = crate,ratatui,inbetween,iterm,fo,brunch - diff --git a/reference/atuin-18.16.1/.depot/workflows/codespell.yml b/reference/atuin-18.16.1/.depot/workflows/codespell.yml deleted file mode 100755 index 1ad4692..0000000 --- a/reference/atuin-18.16.1/.depot/workflows/codespell.yml +++ /dev/null @@ -1,28 +0,0 @@ -# Depot CI Migration -# Source: .github/workflows/codespell.yml -# -# No changes were necessary. - -# Codespell configuration is within .codespellrc -name: Codespell -on: - push: - branches: [main] - pull_request: - branches: [main] -permissions: - contents: read -jobs: - codespell: - name: Check for spelling errors - runs-on: depot-ubuntu-24.04 - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Codespell - uses: codespell-project/actions-codespell@v2 - with: - # This is regenerated from commit history - # we cannot rewrite commit history, and I'd rather not correct it - # every time - exclude_file: CHANGELOG.md diff --git a/reference/atuin-18.16.1/.depot/workflows/installer.yml b/reference/atuin-18.16.1/.depot/workflows/installer.yml deleted file mode 100755 index da09ec5..0000000 --- a/reference/atuin-18.16.1/.depot/workflows/installer.yml +++ /dev/null @@ -1,36 +0,0 @@ -# Depot CI Migration -# Source: .github/workflows/installer.yml -# -# No changes were necessary. - -name: Install -on: - push: - branches: [main] - pull_request: - paths: .github/workflows/installer.yml -env: - CARGO_TERM_COLOR: always -jobs: - install: - strategy: - matrix: - os: [depot-ubuntu-24.04, macos-14] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v6 - - name: Install zsh for ubuntu - if: matrix.os == 'depot-ubuntu-24.04' - run: | - sudo apt install zsh - - name: Test install script on bash - run: | - /bin/bash -c "$(curl --proto '=https' --tlsv1.2 -sSf https://setup.atuin.sh)" - [ -d "$HOME/.atuin" ] && source $HOME/.atuin/bin/env - atuin --help - - name: Test install script on zsh - shell: zsh {0} - run: | - /bin/bash -c "$(curl --proto '=https' --tlsv1.2 -sSf https://setup.atuin.sh)" - [ -d "$HOME/.atuin" ] && source $HOME/.atuin/bin/env - atuin --help diff --git a/reference/atuin-18.16.1/.depot/workflows/nix.yml b/reference/atuin-18.16.1/.depot/workflows/nix.yml deleted file mode 100755 index 571688b..0000000 --- a/reference/atuin-18.16.1/.depot/workflows/nix.yml +++ /dev/null @@ -1,33 +0,0 @@ -# Depot CI Migration -# Source: .github/workflows/nix.yml -# -# No changes were necessary. - -# Verify the Nix build is working -# Failures will usually occur due to an out of date Rust version -# That can be updated to the latest version in nixpkgs-unstable with `nix flake update` -name: Nix -on: - push: - branches: [main] - paths-ignore: - - 'ui/**' - pull_request: - branches: [main] - paths-ignore: - - 'ui/**' -jobs: - check: - runs-on: depot-ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - uses: cachix/install-nix-action@v31 - - name: Run nix flake check - run: nix flake check --print-build-logs - build-test: - runs-on: depot-ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - uses: cachix/install-nix-action@v31 - - name: Run nix build - run: nix build --print-build-logs diff --git a/reference/atuin-18.16.1/.depot/workflows/rust.yml b/reference/atuin-18.16.1/.depot/workflows/rust.yml deleted file mode 100755 index 8dfdad1..0000000 --- a/reference/atuin-18.16.1/.depot/workflows/rust.yml +++ /dev/null @@ -1,187 +0,0 @@ -# Depot CI Migration -# Source: .github/workflows/rust.yml -# -# No changes were necessary. - -name: Rust -on: - push: - branches: [main] - paths-ignore: - - "ui/**" - pull_request: - branches: [main] - paths-ignore: - - "ui/**" -env: - CARGO_TERM_COLOR: always -jobs: - build: - strategy: - matrix: - os: [depot-ubuntu-24.04, macos-14, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v6 - - name: Install rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.94.0 - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }} - - name: Run cargo build common - run: cargo build -p atuin-common --locked --release - - name: Run cargo build client - run: cargo build -p atuin-client --locked --release - - name: Run cargo build server - run: cargo build -p atuin-server --locked --release - - name: Run cargo build main - run: cargo build --all --locked --release - cross-compile: - strategy: - matrix: - # There was an attempt to make cross-compiles also work on FreeBSD, but that failed with: - # - # warning: libelf.so.2, needed by <...>/libkvm.so, not found (try using -rpath or -rpath-link) - target: [x86_64-unknown-illumos] - runs-on: depot-ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - name: Install cross - uses: taiki-e/install-action@v2 - with: - tool: cross - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ matrix.target }}-cross-compile-${{ hashFiles('**/Cargo.lock') }} - - name: Run cross build common - run: cross build -p atuin-common --locked --target ${{ matrix.target }} - - name: Run cross build client - run: cross build -p atuin-client --locked --target ${{ matrix.target }} - - name: Run cross build server - run: cross build -p atuin-server --locked --target ${{ matrix.target }} - - name: Run cross build main - run: | - cross build --all --locked --target ${{ matrix.target }} - unit-test: - strategy: - matrix: - os: [depot-ubuntu-24.04, macos-14, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v6 - - name: Install rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.94.0 - - uses: taiki-e/install-action@v2 - name: Install nextest - with: - tool: cargo-nextest - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-debug-${{ hashFiles('**/Cargo.lock') }} - - name: Run cargo test - run: cargo nextest run --lib --bins - check: - strategy: - matrix: - os: [depot-ubuntu-24.04, macos-14, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v6 - - name: Install rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.94.0 - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-debug-${{ hashFiles('**/Cargo.lock') }} - - name: Run cargo check (all features) - run: cargo check --all-features --workspace - - name: Run cargo check (no features) - run: cargo check --no-default-features --workspace - - name: Run cargo check (sync) - run: cargo check --no-default-features --features sync --workspace - - name: Run cargo check (server) - run: cargo check -p atuin-server - - name: Run cargo check (client only) - run: cargo check --no-default-features --features client --workspace - integration-test: - runs-on: depot-ubuntu-24.04 - services: - postgres: - image: postgres - env: - POSTGRES_USER: atuin - POSTGRES_PASSWORD: pass - POSTGRES_DB: atuin - ports: - - 5432:5432 - steps: - - uses: actions/checkout@v6 - - name: Install rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.94.0 - - uses: taiki-e/install-action@v2 - name: Install nextest - with: - tool: cargo-nextest - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-debug-${{ hashFiles('**/Cargo.lock') }} - - name: Run cargo test - run: cargo nextest run --test '*' - env: - ATUIN_DB_URI: postgres://atuin:pass@localhost:5432/atuin - clippy: - runs-on: depot-ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - name: Install latest rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.94.0 - components: clippy - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-debug-${{ hashFiles('**/Cargo.lock') }} - - name: Run clippy - run: cargo clippy -- -D warnings -D clippy::redundant_clone - format: - runs-on: depot-ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - name: Install latest rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.94.0 - components: rustfmt - - name: Format - run: cargo fmt -- --check diff --git a/reference/atuin-18.16.1/.depot/workflows/shellcheck.yml b/reference/atuin-18.16.1/.depot/workflows/shellcheck.yml deleted file mode 100755 index 28c16c2..0000000 --- a/reference/atuin-18.16.1/.depot/workflows/shellcheck.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Depot CI Migration -# Source: .github/workflows/shellcheck.yml -# -# No changes were necessary. - -name: Shellcheck -on: - push: - branches: [main] - pull_request: - branches: [main] -jobs: - shellcheck: - runs-on: depot-ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - name: Run shellcheck - uses: ludeeus/action-shellcheck@master - env: - SHELLCHECK_OPTS: "-e SC2148" diff --git a/reference/atuin-18.16.1/.depot/workflows/update-nix-deps.yml b/reference/atuin-18.16.1/.depot/workflows/update-nix-deps.yml deleted file mode 100755 index 96a35cd..0000000 --- a/reference/atuin-18.16.1/.depot/workflows/update-nix-deps.yml +++ /dev/null @@ -1,25 +0,0 @@ -# Depot CI Migration -# Source: .github/workflows/update-nix-deps.yml -# -# No changes were necessary. - -name: Update Nix Deps -on: - workflow_dispatch: # allows manual triggering - schedule: - - cron: '0 0 1 * *' # runs monthly on the first day of the month at 00:00 -jobs: - lockfile: - runs-on: depot-ubuntu-24.04 - if: github.repository == 'atuinsh/atuin' - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@main - - name: Update flake.lock - uses: DeterminateSystems/update-flake-lock@main - with: - pr-title: "chore(deps): update flake.lock" - pr-labels: | - dependencies diff --git a/reference/atuin-18.16.1/.dockerignore b/reference/atuin-18.16.1/.dockerignore deleted file mode 100755 index 91e4ecf..0000000 --- a/reference/atuin-18.16.1/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -./target -Dockerfile diff --git a/reference/atuin-18.16.1/.gitattributes b/reference/atuin-18.16.1/.gitattributes deleted file mode 100755 index 95eedf9..0000000 --- a/reference/atuin-18.16.1/.gitattributes +++ /dev/null @@ -1,5 +0,0 @@ -*.sh eol=lf -*.nix eol=lf -*.zsh eol=lf - -*.sql eol=lf diff --git a/reference/atuin-18.16.1/.github/DISCUSSION_TEMPLATE/support.yml b/reference/atuin-18.16.1/.github/DISCUSSION_TEMPLATE/support.yml deleted file mode 100755 index 5dd663e..0000000 --- a/reference/atuin-18.16.1/.github/DISCUSSION_TEMPLATE/support.yml +++ /dev/null @@ -1,84 +0,0 @@ -body: - - type: input - attributes: - label: Operating System - description: What operating system are you using? - placeholder: "Example: macOS Big Sur" - validations: - required: true - - - type: input - attributes: - label: Shell - description: What shell are you using? - placeholder: "Example: zsh 5.8.1" - validations: - required: true - - - type: dropdown - attributes: - label: Version - description: What version of atuin are you running? - multiple: false - options: # how often will I forget to update this? a lot. - - v17.0.0 (Default) - - v16.0.0 - - v15.0.0 - - v14.0.1 - - v14.0.0 - - v13.0.1 - - v13.0.0 - - v12.0.0 - - v11.0.0 - - v0.10.0 - - v0.9.1 - - v0.9.0 - - v0.8.1 - - v0.8.0 - - v0.7.2 - - v0.7.1 - - v0.7.0 - - v0.6.4 - - v0.6.3 - default: 0 - validations: - required: true - - - type: checkboxes - attributes: - label: Self hosted - description: Are you self hosting atuin server? - options: - - label: I am self hosting atuin server - - - type: checkboxes - attributes: - label: Search the issues - description: Did you search the issues and discussions for your problem? - options: - - label: I checked that someone hasn't already asked about the same issue - required: true - - - type: textarea - attributes: - label: Behaviour - description: "Please describe the issue - what you expected to happen, what actually happened" - - - type: textarea - attributes: - label: Logs - description: "If possible, please include logs from atuin, especially if you self host the server - ATUIN_LOG=debug" - - - type: textarea - attributes: - label: Extra information - description: "Anything else you'd like to add?" - - - type: checkboxes - attributes: - label: Code of Conduct - description: The Code of Conduct helps create a safe space for everyone. We require - that everyone agrees to it. - options: - - label: I agree to follow this project's [Code of Conduct](https://github.com/atuinsh/atuin/blob/main/CODE_OF_CONDUCT.md) - required: true diff --git a/reference/atuin-18.16.1/.github/FUNDING.yml b/reference/atuin-18.16.1/.github/FUNDING.yml deleted file mode 100755 index bbbb246..0000000 --- a/reference/atuin-18.16.1/.github/FUNDING.yml +++ /dev/null @@ -1,13 +0,0 @@ -# These are supported funding model platforms - -github: [atuinsh] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/reference/atuin-18.16.1/.github/ISSUE_TEMPLATE/bug.yaml b/reference/atuin-18.16.1/.github/ISSUE_TEMPLATE/bug.yaml deleted file mode 100755 index d1b8e64..0000000 --- a/reference/atuin-18.16.1/.github/ISSUE_TEMPLATE/bug.yaml +++ /dev/null @@ -1,39 +0,0 @@ -name: Bug Report -description: File a bug report -title: "[Bug]: " -labels: ["bug", "triage"] -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out this bug report! - - type: textarea - id: what-expected - attributes: - label: What did you expect to happen? - placeholder: Tell us what you expected to see! - validations: - required: true - - type: textarea - id: what-happened - attributes: - label: What happened? - placeholder: Tell us what you see! - validations: - required: true - - type: textarea - id: doctor - validations: - required: true - attributes: - label: Atuin doctor output - description: Please run 'atuin doctor' and share the output. If it fails to run, share any errors. This requires Atuin >=v18.1.0 - render: yaml - - type: checkboxes - id: terms - attributes: - label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/atuinsh/atuin/blob/main/CODE_OF_CONDUCT.md) - options: - - label: I agree to follow this project's Code of Conduct - required: true diff --git a/reference/atuin-18.16.1/.github/dependabot.yml b/reference/atuin-18.16.1/.github/dependabot.yml deleted file mode 100755 index 6091393..0000000 --- a/reference/atuin-18.16.1/.github/dependabot.yml +++ /dev/null @@ -1,19 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: "cargo" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" - - package-ecosystem: "docker" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" diff --git a/reference/atuin-18.16.1/.github/pull_request_template.md b/reference/atuin-18.16.1/.github/pull_request_template.md deleted file mode 100755 index a037ef0..0000000 --- a/reference/atuin-18.16.1/.github/pull_request_template.md +++ /dev/null @@ -1,5 +0,0 @@ - - -## Checks -- [ ] I am happy for maintainers to push small adjustments to this PR, to speed up the review cycle -- [ ] I have checked that there are no existing pull requests for the same thing diff --git a/reference/atuin-18.16.1/.github/workflows/codespell.yml b/reference/atuin-18.16.1/.github/workflows/codespell.yml deleted file mode 100755 index d39dd87..0000000 --- a/reference/atuin-18.16.1/.github/workflows/codespell.yml +++ /dev/null @@ -1,28 +0,0 @@ -# Codespell configuration is within .codespellrc ---- -name: Codespell - -on: - push: - branches: [main] - pull_request: - branches: [main] - -permissions: - contents: read - -jobs: - codespell: - name: Check for spelling errors - runs-on: depot-ubuntu-24.04 - - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Codespell - uses: codespell-project/actions-codespell@v2 - with: - # This is regenerated from commit history - # we cannot rewrite commit history, and I'd rather not correct it - # every time - exclude_file: CHANGELOG.md diff --git a/reference/atuin-18.16.1/.github/workflows/docker.yaml b/reference/atuin-18.16.1/.github/workflows/docker.yaml deleted file mode 100755 index 9dd687c..0000000 --- a/reference/atuin-18.16.1/.github/workflows/docker.yaml +++ /dev/null @@ -1,61 +0,0 @@ -name: build-docker - -on: - push: - branches: [main] - tags: - - 'v*' - -jobs: - publish: - concurrency: - group: ${{ github.ref }}-docker - cancel-in-progress: true - permissions: - packages: write - contents: read - id-token: write - - runs-on: depot-ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - - name: Get Repo Owner - id: get_repo_owner - run: echo "REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" > $GITHUB_ENV - - - uses: depot/setup-action@v1 - - - name: Login to container Registry - uses: docker/login-action@v3 - with: - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - registry: ghcr.io - - - name: Docker meta - id: meta - uses: docker/metadata-action@v5 - with: - images: ghcr.io/${{ env.REPO_OWNER }}/atuin - flavor: | - latest=false - tags: | - type=ref,event=branch - type=sha,prefix= - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - - name: Build and push - uses: depot/build-push-action@v1 - with: - push: true - platforms: linux/amd64,linux/arm64 - file: ./Dockerfile - context: . - provenance: false - build-args: | - Version=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] || 'dev' }} - GitCommit=${{ github.sha }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} diff --git a/reference/atuin-18.16.1/.github/workflows/installer.yml b/reference/atuin-18.16.1/.github/workflows/installer.yml deleted file mode 100755 index c4c4aab..0000000 --- a/reference/atuin-18.16.1/.github/workflows/installer.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Install - -on: - push: - branches: [main] - pull_request: - paths: .github/workflows/installer.yml - -env: - CARGO_TERM_COLOR: always - -jobs: - install: - strategy: - matrix: - os: [depot-ubuntu-24.04, macos-14] - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v6 - - - name: Install zsh for ubuntu - if: matrix.os == 'depot-ubuntu-24.04' - run: | - sudo apt install zsh - - - name: Test install script on bash - run: | - /bin/bash -c "$(curl --proto '=https' --tlsv1.2 -sSf https://setup.atuin.sh)" - [ -d "$HOME/.atuin" ] && source $HOME/.atuin/bin/env - atuin --help - - - name: Test install script on zsh - shell: zsh {0} - run: | - /bin/bash -c "$(curl --proto '=https' --tlsv1.2 -sSf https://setup.atuin.sh)" - [ -d "$HOME/.atuin" ] && source $HOME/.atuin/bin/env - atuin --help diff --git a/reference/atuin-18.16.1/.github/workflows/nix.yml b/reference/atuin-18.16.1/.github/workflows/nix.yml deleted file mode 100755 index 6e763d4..0000000 --- a/reference/atuin-18.16.1/.github/workflows/nix.yml +++ /dev/null @@ -1,34 +0,0 @@ -# Verify the Nix build is working -# Failures will usually occur due to an out of date Rust version -# That can be updated to the latest version in nixpkgs-unstable with `nix flake update` -name: Nix -on: - push: - branches: [ main ] - paths-ignore: - - 'ui/**' - pull_request: - branches: [ main ] - paths-ignore: - - 'ui/**' - -jobs: - check: - runs-on: depot-ubuntu-24.04 - - steps: - - uses: actions/checkout@v6 - - uses: cachix/install-nix-action@v31 - - - name: Run nix flake check - run: nix flake check --print-build-logs - - build-test: - runs-on: depot-ubuntu-24.04 - - steps: - - uses: actions/checkout@v6 - - uses: cachix/install-nix-action@v31 - - - name: Run nix build - run: nix build --print-build-logs diff --git a/reference/atuin-18.16.1/.github/workflows/release.yml b/reference/atuin-18.16.1/.github/workflows/release.yml deleted file mode 100755 index d742a92..0000000 --- a/reference/atuin-18.16.1/.github/workflows/release.yml +++ /dev/null @@ -1,304 +0,0 @@ -# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist -# -# Copyright 2022-2024, axodotdev -# SPDX-License-Identifier: MIT or Apache-2.0 -# -# CI that: -# -# * checks for a Git Tag that looks like a release -# * builds artifacts with dist (archives, installers, hashes) -# * uploads those artifacts to temporary workflow zip -# * on success, uploads the artifacts to a GitHub Release -# -# Note that the GitHub Release will be created with a generated -# title/body based on your changelogs. - -name: Release -permissions: - "contents": "write" - -# This task will run whenever you push a git tag that looks like a version -# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. -# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where -# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION -# must be a Cargo-style SemVer Version (must have at least major.minor.patch). -# -# If PACKAGE_NAME is specified, then the announcement will be for that -# package (erroring out if it doesn't have the given version or isn't dist-able). -# -# If PACKAGE_NAME isn't specified, then the announcement will be for all -# (dist-able) packages in the workspace with that version (this mode is -# intended for workspaces with only one dist-able package, or with all dist-able -# packages versioned/released in lockstep). -# -# If you push multiple tags at once, separate instances of this workflow will -# spin up, creating an independent announcement for each one. However, GitHub -# will hard limit this to 3 tags per commit, as it will assume more tags is a -# mistake. -# -# If there's a prerelease-style suffix to the version, then the release(s) -# will be marked as a prerelease. -on: - pull_request: - push: - tags: - - '**[0-9]+.[0-9]+.[0-9]+*' - -jobs: - # Run 'dist plan' (or host) to determine what tasks we need to do - plan: - runs-on: "ubuntu-22.04" - outputs: - val: ${{ steps.plan.outputs.manifest }} - tag: ${{ !github.event.pull_request && github.ref_name || '' }} - tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} - publishing: ${{ !github.event.pull_request }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - submodules: recursive - - name: Install dist - # we specify bash to get pipefail; it guards against the `curl` command - # failing. otherwise `sh` won't catch that `curl` returned non-0 - shell: bash - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.31.0/cargo-dist-installer.sh | sh" - - name: Cache dist - uses: actions/upload-artifact@v6 - with: - name: cargo-dist-cache - path: ~/.cargo/bin/dist - # sure would be cool if github gave us proper conditionals... - # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible - # functionality based on whether this is a pull_request, and whether it's from a fork. - # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* - # but also really annoying to build CI around when it needs secrets to work right.) - - id: plan - run: | - dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json - echo "dist ran successfully" - cat plan-dist-manifest.json - echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - - name: "Upload dist-manifest.json" - uses: actions/upload-artifact@v6 - with: - name: artifacts-plan-dist-manifest - path: plan-dist-manifest.json - - # Build and packages all the platform-specific things - build-local-artifacts: - name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) - # Let the initial task tell us to not run (currently very blunt) - needs: - - plan - if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} - strategy: - fail-fast: false - # Target platforms/runners are computed by dist in create-release. - # Each member of the matrix has the following arguments: - # - # - runner: the github runner - # - dist-args: cli flags to pass to dist - # - install-dist: expression to run to install dist on the runner - # - # Typically there will be: - # - 1 "global" task that builds universal installers - # - N "local" tasks that build each platform's binaries and platform-specific installers - matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} - runs-on: ${{ matrix.runner }} - container: ${{ matrix.container && matrix.container.image || null }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json - permissions: - "attestations": "write" - "contents": "read" - "id-token": "write" - steps: - - name: enable windows longpaths - run: | - git config --global core.longpaths true - - uses: actions/checkout@v6 - with: - persist-credentials: false - submodules: recursive - - name: Install Rust non-interactively if not already installed - if: ${{ matrix.container }} - run: | - if ! command -v cargo > /dev/null 2>&1; then - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - fi - - name: Install dist - run: ${{ matrix.install_dist.run }} - # Get the dist-manifest - - name: Fetch local artifacts - uses: actions/download-artifact@v7 - with: - pattern: artifacts-* - path: target/distrib/ - merge-multiple: true - - name: Install dependencies - run: | - ${{ matrix.packages_install }} - - name: Build artifacts - run: | - # Actually do builds and make zips and whatnot - dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json - echo "dist ran successfully" - - name: Attest - uses: actions/attest-build-provenance@v3 - with: - subject-path: "target/distrib/*${{ join(matrix.targets, ', ') }}*" - - id: cargo-dist - name: Post-build - # We force bash here just because github makes it really hard to get values up - # to "real" actions without writing to env-vars, and writing to env-vars has - # inconsistent syntax between shell and powershell. - shell: bash - run: | - # Parse out what we just built and upload it to scratch storage - echo "paths<> "$GITHUB_OUTPUT" - dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" - echo "EOF" >> "$GITHUB_OUTPUT" - - cp dist-manifest.json "$BUILD_MANIFEST_NAME" - - name: "Upload artifacts" - uses: actions/upload-artifact@v6 - with: - name: artifacts-build-local-${{ join(matrix.targets, '_') }} - path: | - ${{ steps.cargo-dist.outputs.paths }} - ${{ env.BUILD_MANIFEST_NAME }} - - # Build and package all the platform-agnostic(ish) things - build-global-artifacts: - needs: - - plan - - build-local-artifacts - runs-on: "ubuntu-22.04" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - submodules: recursive - - name: Install cached dist - uses: actions/download-artifact@v7 - with: - name: cargo-dist-cache - path: ~/.cargo/bin/ - - run: chmod +x ~/.cargo/bin/dist - # Get all the local artifacts for the global tasks to use (for e.g. checksums) - - name: Fetch local artifacts - uses: actions/download-artifact@v7 - with: - pattern: artifacts-* - path: target/distrib/ - merge-multiple: true - - id: cargo-dist - shell: bash - run: | - dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json - echo "dist ran successfully" - - # Parse out what we just built and upload it to scratch storage - echo "paths<> "$GITHUB_OUTPUT" - jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" - echo "EOF" >> "$GITHUB_OUTPUT" - - cp dist-manifest.json "$BUILD_MANIFEST_NAME" - - name: "Upload artifacts" - uses: actions/upload-artifact@v6 - with: - name: artifacts-build-global - path: | - ${{ steps.cargo-dist.outputs.paths }} - ${{ env.BUILD_MANIFEST_NAME }} - # Determines if we should publish/announce - host: - needs: - - plan - - build-local-artifacts - - build-global-artifacts - # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) - if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - runs-on: "ubuntu-22.04" - outputs: - val: ${{ steps.host.outputs.manifest }} - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - submodules: recursive - - name: Install cached dist - uses: actions/download-artifact@v7 - with: - name: cargo-dist-cache - path: ~/.cargo/bin/ - - run: chmod +x ~/.cargo/bin/dist - # Fetch artifacts from scratch-storage - - name: Fetch artifacts - uses: actions/download-artifact@v7 - with: - pattern: artifacts-* - path: target/distrib/ - merge-multiple: true - - id: host - shell: bash - run: | - dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json - echo "artifacts uploaded and released successfully" - cat dist-manifest.json - echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - - name: "Upload dist-manifest.json" - uses: actions/upload-artifact@v6 - with: - # Overwrite the previous copy - name: artifacts-dist-manifest - path: dist-manifest.json - # Create a GitHub Release while uploading all files to it - - name: "Download GitHub Artifacts" - uses: actions/download-artifact@v7 - with: - pattern: artifacts-* - path: artifacts - merge-multiple: true - - name: Cleanup - run: | - # Remove the granular manifests - rm -f artifacts/*-dist-manifest.json - - name: Create GitHub Release - env: - PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" - ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" - ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" - RELEASE_COMMIT: "${{ github.sha }}" - run: | - # Write and read notes from a file to avoid quoting breaking things - echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt - - gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* - - announce: - needs: - - plan - - host - # use "always() && ..." to allow us to wait for all publish jobs while - # still allowing individual publish jobs to skip themselves (for prereleases). - # "host" however must run to completion, no skipping allowed! - if: ${{ always() && needs.host.result == 'success' }} - runs-on: "ubuntu-22.04" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - submodules: recursive diff --git a/reference/atuin-18.16.1/.github/workflows/rust.yml b/reference/atuin-18.16.1/.github/workflows/rust.yml deleted file mode 100755 index 1941e0a..0000000 --- a/reference/atuin-18.16.1/.github/workflows/rust.yml +++ /dev/null @@ -1,230 +0,0 @@ -name: Rust - -on: - push: - branches: [main] - paths-ignore: - - "ui/**" - pull_request: - branches: [main] - paths-ignore: - - "ui/**" - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - strategy: - matrix: - os: [depot-ubuntu-24.04, macos-14, windows-latest] - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v6 - - - name: Install rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.95.0 - - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }} - - - name: Run cargo build common - run: cargo build -p atuin-common --locked --release - - - name: Run cargo build client - run: cargo build -p atuin-client --locked --release - - - name: Run cargo build server - run: cargo build -p atuin-server --locked --release - - - name: Run cargo build main - run: cargo build --all --locked --release - - cross-compile: - strategy: - matrix: - # There was an attempt to make cross-compiles also work on FreeBSD, but that failed with: - # - # warning: libelf.so.2, needed by <...>/libkvm.so, not found (try using -rpath or -rpath-link) - target: [x86_64-unknown-illumos] - runs-on: depot-ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - - - name: Install cross - uses: taiki-e/install-action@v2 - with: - tool: cross - - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ matrix.target }}-cross-compile-${{ hashFiles('**/Cargo.lock') }} - - - name: Run cross build common - run: cross build -p atuin-common --locked --target ${{ matrix.target }} - - - name: Run cross build client - run: cross build -p atuin-client --locked --target ${{ matrix.target }} - - - name: Run cross build server - run: cross build -p atuin-server --locked --target ${{ matrix.target }} - - - name: Run cross build main - run: | - cross build --all --locked --target ${{ matrix.target }} - - unit-test: - strategy: - matrix: - os: [depot-ubuntu-24.04, macos-14, windows-latest] - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v6 - - - name: Install rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.95.0 - - - uses: taiki-e/install-action@v2 - name: Install nextest - with: - tool: cargo-nextest - - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-debug-${{ hashFiles('**/Cargo.lock') }} - - - name: Run cargo test - run: cargo nextest run --lib --bins - - check: - strategy: - matrix: - os: [depot-ubuntu-24.04, macos-14, windows-latest] - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v6 - - - name: Install rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.95.0 - - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-debug-${{ hashFiles('**/Cargo.lock') }} - - - name: Run cargo check (all features) - run: cargo check --all-features --workspace - - - name: Run cargo check (no features) - run: cargo check --no-default-features --workspace - - - name: Run cargo check (sync) - run: cargo check --no-default-features --features sync --workspace - - - name: Run cargo check (server) - run: cargo check -p atuin-server - - - name: Run cargo check (client only) - run: cargo check --no-default-features --features client --workspace - - integration-test: - runs-on: depot-ubuntu-24.04 - - services: - postgres: - image: postgres - env: - POSTGRES_USER: atuin - POSTGRES_PASSWORD: pass - POSTGRES_DB: atuin - ports: - - 5432:5432 - - steps: - - uses: actions/checkout@v6 - - - name: Install rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.95.0 - - - uses: taiki-e/install-action@v2 - name: Install nextest - with: - tool: cargo-nextest - - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-debug-${{ hashFiles('**/Cargo.lock') }} - - - name: Run cargo test - run: cargo nextest run --test '*' - env: - ATUIN_DB_URI: postgres://atuin:pass@localhost:5432/atuin - - clippy: - runs-on: depot-ubuntu-24.04 - - steps: - - uses: actions/checkout@v6 - - - name: Install latest rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.95.0 - components: clippy - - - uses: actions/cache@v5 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-debug-${{ hashFiles('**/Cargo.lock') }} - - - name: Run clippy - run: cargo clippy -- -D warnings -D clippy::redundant_clone - - format: - runs-on: depot-ubuntu-24.04 - - steps: - - uses: actions/checkout@v6 - - - name: Install latest rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.95.0 - components: rustfmt - - - name: Format - run: cargo fmt -- --check diff --git a/reference/atuin-18.16.1/.github/workflows/shellcheck.yml b/reference/atuin-18.16.1/.github/workflows/shellcheck.yml deleted file mode 100755 index 12abbeb..0000000 --- a/reference/atuin-18.16.1/.github/workflows/shellcheck.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Shellcheck - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - shellcheck: - runs-on: depot-ubuntu-24.04 - - steps: - - uses: actions/checkout@v6 - - name: Run shellcheck - uses: ludeeus/action-shellcheck@master - env: - SHELLCHECK_OPTS: "-e SC2148" diff --git a/reference/atuin-18.16.1/.github/workflows/update-nix-deps.yml b/reference/atuin-18.16.1/.github/workflows/update-nix-deps.yml deleted file mode 100755 index a6d65c4..0000000 --- a/reference/atuin-18.16.1/.github/workflows/update-nix-deps.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Update Nix Deps -on: - workflow_dispatch: # allows manual triggering - schedule: - - cron: '0 0 1 * *' # runs monthly on the first day of the month at 00:00 - -jobs: - lockfile: - runs-on: depot-ubuntu-24.04 - if: github.repository == 'atuinsh/atuin' - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@main - - name: Update flake.lock - uses: DeterminateSystems/update-flake-lock@main - with: - pr-title: "chore(deps): update flake.lock" - pr-labels: | - dependencies diff --git a/reference/atuin-18.16.1/.gitignore b/reference/atuin-18.16.1/.gitignore deleted file mode 100755 index 78ae9ef..0000000 --- a/reference/atuin-18.16.1/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -.DS_Store -/target -*/target -.env -.idea/ -.vscode/ -result -publish.sh -.envrc -.planning/ - -ui/backend/target -ui/backend/gen - -sqlite-server.db* - -.atuin/permissions.*.toml diff --git a/reference/atuin-18.16.1/.gitkeep b/reference/atuin-18.16.1/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/reference/atuin-18.16.1/.mailmap b/reference/atuin-18.16.1/.mailmap deleted file mode 100755 index e408a9b..0000000 --- a/reference/atuin-18.16.1/.mailmap +++ /dev/null @@ -1,14 +0,0 @@ -networkException -Violet Shreve -Chris Rose -Conrad Ludgate -Cristian Le -Dennis Trautwein -Ellie Huxtable -Ellie Huxtable -Frank Hamand -Jakob Schrettenbrunner -Nemo157 -Richard de Boer -Sandro -TymanWasTaken diff --git a/reference/atuin-18.16.1/.rustfmt.toml b/reference/atuin-18.16.1/.rustfmt.toml deleted file mode 100755 index 0e363b3..0000000 --- a/reference/atuin-18.16.1/.rustfmt.toml +++ /dev/null @@ -1,4 +0,0 @@ -reorder_imports = true -# uncomment once stable -#imports_granularity = "crate" -#group_imports = "StdExternalCrate" diff --git a/reference/claude-code-2.1.88-leak/.gitkeep b/reference/claude-code-2.1.88-leak/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/reference/claude-code-cli/.gitkeep b/reference/claude-code-cli/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/reference/claude-hud-0.0.12/.claude-plugin/marketplace.json b/reference/claude-hud-0.0.12/.claude-plugin/marketplace.json deleted file mode 100755 index e953105..0000000 --- a/reference/claude-hud-0.0.12/.claude-plugin/marketplace.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "claude-hud", - "owner": { - "name": "Jarrod Watts", - "email": "jarrodwattsyt@gmail.com" - }, - "metadata": { - "description": "Real-time statusline HUD for Claude Code - context health, tool activity, agent tracking, and todo progress", - "version": "0.0.12" - }, - "plugins": [ - { - "name": "claude-hud", - "source": "./", - "description": "Real-time statusline showing context usage, active tools, running agents, and todo progress. Always visible below your input, zero config required.", - "category": "monitoring", - "tags": ["hud", "statusline", "monitoring", "context", "tools", "agents", "todos"] - } - ] -} diff --git a/reference/claude-hud-0.0.12/.claude-plugin/plugin.json b/reference/claude-hud-0.0.12/.claude-plugin/plugin.json deleted file mode 100755 index e416b20..0000000 --- a/reference/claude-hud-0.0.12/.claude-plugin/plugin.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "claude-hud", - "description": "Real-time statusline HUD for Claude Code - context health, tool activity, agent tracking, and todo progress", - "version": "0.0.12", - "author": { - "name": "Jarrod Watts", - "url": "https://github.com/jarrodwatts" - }, - "commands": [ - "./commands/setup.md", - "./commands/configure.md" - ], - "homepage": "https://github.com/jarrodwatts/claude-hud", - "repository": "https://github.com/jarrodwatts/claude-hud", - "license": "MIT", - "keywords": ["hud", "monitoring", "statusline", "context", "tools", "agents", "todos", "claude-code"] -} diff --git a/reference/claude-hud-0.0.12/.editorconfig b/reference/claude-hud-0.0.12/.editorconfig deleted file mode 100755 index c63be93..0000000 --- a/reference/claude-hud-0.0.12/.editorconfig +++ /dev/null @@ -1,12 +0,0 @@ -root = true - -[*] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -indent_style = space -indent_size = 2 -trim_trailing_whitespace = true - -[*.md] -trim_trailing_whitespace = false diff --git a/reference/claude-hud-0.0.12/.github/CODEOWNERS b/reference/claude-hud-0.0.12/.github/CODEOWNERS deleted file mode 100755 index 0cc2fbf..0000000 --- a/reference/claude-hud-0.0.12/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @jarrodwatts diff --git a/reference/claude-hud-0.0.12/.github/ISSUE_TEMPLATE/bug_report.md b/reference/claude-hud-0.0.12/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100755 index 2c923fb..0000000 --- a/reference/claude-hud-0.0.12/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Bug report -about: Report a reproducible problem -labels: bug ---- - -## Summary - -## Steps to Reproduce - -## Expected Behavior - -## Actual Behavior - -## Environment - -- OS: -- Node/Bun version: -- Claude Code version: - -## Logs or Screenshots diff --git a/reference/claude-hud-0.0.12/.github/ISSUE_TEMPLATE/config.yml b/reference/claude-hud-0.0.12/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100755 index bff1611..0000000 --- a/reference/claude-hud-0.0.12/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Security report - url: mailto:jarrodwttsyt@gmail.com - about: Please report security vulnerabilities via email. diff --git a/reference/claude-hud-0.0.12/.github/ISSUE_TEMPLATE/feature_request.md b/reference/claude-hud-0.0.12/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100755 index 11235dd..0000000 --- a/reference/claude-hud-0.0.12/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: Feature request -about: Suggest an idea or enhancement -labels: enhancement ---- - -## Summary - -## Problem to Solve - -## Proposed Solution - -## Alternatives Considered - -## Additional Context diff --git a/reference/claude-hud-0.0.12/.github/dependabot.yml b/reference/claude-hud-0.0.12/.github/dependabot.yml deleted file mode 100755 index 4796d06..0000000 --- a/reference/claude-hud-0.0.12/.github/dependabot.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "npm" - directory: "/" - schedule: - interval: "weekly" - open-pull-requests-limit: 5 diff --git a/reference/claude-hud-0.0.12/.github/pull_request_template.md b/reference/claude-hud-0.0.12/.github/pull_request_template.md deleted file mode 100755 index a4362ad..0000000 --- a/reference/claude-hud-0.0.12/.github/pull_request_template.md +++ /dev/null @@ -1,11 +0,0 @@ -## Summary - -## Testing - -- [ ] `npm test` -- [ ] `npm run test:coverage` - -## Checklist - -- [ ] Tests updated or not needed -- [ ] Docs updated if behavior changed diff --git a/reference/claude-hud-0.0.12/.github/workflows/build-dist.yml b/reference/claude-hud-0.0.12/.github/workflows/build-dist.yml deleted file mode 100755 index 8e192b1..0000000 --- a/reference/claude-hud-0.0.12/.github/workflows/build-dist.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Build dist - -on: - push: - branches: [main] - -concurrency: - group: build-dist - cancel-in-progress: false - -permissions: - contents: write - -jobs: - build: - runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, '[auto]')" - - steps: - - uses: actions/checkout@v6 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/setup-node@v6 - with: - node-version: '20' - cache: 'npm' - - - run: npm ci - - run: npm test - - run: npm run build - - - name: Verify build output - run: test -f dist/index.js || exit 1 - - - name: Commit dist/ - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add dist/ --force - git diff --staged --quiet || git commit -m "build: compile dist/ [auto]" - git push diff --git a/reference/claude-hud-0.0.12/.github/workflows/ci.yml b/reference/claude-hud-0.0.12/.github/workflows/ci.yml deleted file mode 100755 index ac2c4db..0000000 --- a/reference/claude-hud-0.0.12/.github/workflows/ci.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: CI - -on: - pull_request: - push: - branches: [main] - paths-ignore: - - 'dist/**' - -jobs: - test: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x, 20.x] - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-node@v6 - with: - node-version: ${{ matrix.node-version }} - cache: npm - - run: npm ci - - run: npm run test:coverage diff --git a/reference/claude-hud-0.0.12/.github/workflows/claude.yml b/reference/claude-hud-0.0.12/.github/workflows/claude.yml deleted file mode 100755 index 5a35acc..0000000 --- a/reference/claude-hud-0.0.12/.github/workflows/claude.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Claude Code - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - issues: - types: [opened, assigned] - pull_request_review: - types: [submitted] - -jobs: - claude: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - issues: read - id-token: write - actions: read # Required for Claude to read CI results on PRs - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 1 - - - name: Run Claude Code - id: claude - uses: anthropics/claude-code-action@v1 - with: - claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - - # This is an optional setting that allows Claude to read CI results on PRs - additional_permissions: | - actions: read - - # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. - # prompt: 'Update the pull request description to include a summary of changes.' - - # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md - claude_args: '--model claude-opus-4-5-20251101' - diff --git a/reference/claude-hud-0.0.12/.github/workflows/release.yml b/reference/claude-hud-0.0.12/.github/workflows/release.yml deleted file mode 100755 index e65a385..0000000 --- a/reference/claude-hud-0.0.12/.github/workflows/release.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Release - -on: - push: - tags: - - "v*.*.*" - -permissions: - contents: write - -jobs: - release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-node@v6 - with: - node-version: 20.x - cache: npm - - run: npm ci - - run: npm run build - - run: npm test - - run: npm run test:coverage - - name: Extract release notes from CHANGELOG - run: | - version="${GITHUB_REF_NAME#v}" - awk -v version="$version" ' - $0 ~ "^## \\[" version "\\]" { in_section = 1; next } - in_section && $0 ~ "^## \\[" { exit } - in_section { print } - ' CHANGELOG.md > RELEASE_NOTES.md - - if [ ! -s RELEASE_NOTES.md ]; then - echo "No changelog section found for version $version" - exit 1 - fi - - name: Create release - uses: softprops/action-gh-release@v2 - with: - body_path: RELEASE_NOTES.md diff --git a/reference/claude-hud-0.0.12/.gitignore b/reference/claude-hud-0.0.12/.gitignore deleted file mode 100755 index 8d2fa88..0000000 --- a/reference/claude-hud-0.0.12/.gitignore +++ /dev/null @@ -1,59 +0,0 @@ -# Dependencies -node_modules/ - -# Build artifacts -# dist/ is gitignored but exists on main - CI builds and commits it after each merge. -# See .github/workflows/build-dist.yml -dist/ -*.tsbuildinfo - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids/ -*.pid -*.seed -*.fifo - -# OS files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# IDE -.idea/ -.vscode/ -*.swp -*.swo -*~ - -# Environment/secrets (safety) -.env -.env.* -.claude/settings.json -.claude/*.local.json -*.pem -*.key -secrets/ -credentials/ - -# Test coverage -coverage/ -.nyc_output/ - -# Temp files -tmp/ -temp/ -*.tmp - -# Lock files (keep package-lock.json for npm) -yarn.lock -bun.lock diff --git a/reference/claude-hud-0.0.12/.gitkeep b/reference/claude-hud-0.0.12/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/reference/hermes-agent-2026.5.16/.dockerignore b/reference/hermes-agent-2026.5.16/.dockerignore deleted file mode 100755 index f4a0248..0000000 --- a/reference/hermes-agent-2026.5.16/.dockerignore +++ /dev/null @@ -1,31 +0,0 @@ -# Git -.git -.gitignore -.gitmodules - -# Dependencies -node_modules -**/node_modules -.venv -**/.venv - -# Built artifacts that are regenerated inside the image. Excluded so local -# rebuilds on the developer's machine don't invalidate the npm-install layer -# that now depends on the full ui-tui/packages/hermes-ink/ tree being present. -ui-tui/dist/ -ui-tui/packages/hermes-ink/dist/ - -# CI/CD -.github - -# Environment files -.env - -*.md - -# Runtime data (bind-mounted at /opt/data; must not leak into build context) -data/ - -# Compose/profile runtime state (bind-mounted; avoid ownership/secret issues) -hermes-config/ -runtime/ diff --git a/reference/hermes-agent-2026.5.16/.env.example b/reference/hermes-agent-2026.5.16/.env.example deleted file mode 100755 index 812986d..0000000 --- a/reference/hermes-agent-2026.5.16/.env.example +++ /dev/null @@ -1,469 +0,0 @@ -# Hermes Agent Environment Configuration -# Copy this file to .env and fill in your API keys - -# ============================================================================= -# LLM PROVIDER (OpenRouter) -# ============================================================================= -# OpenRouter provides access to many models through one API -# All LLM calls go through OpenRouter - no direct provider keys needed -# Get your key at: https://openrouter.ai/keys -# OPENROUTER_API_KEY= - -# Default model is configured in ~/.hermes/config.yaml (model.default). -# Use 'hermes model' or 'hermes setup' to change it. -# LLM_MODEL is no longer read from .env — this line is kept for reference only. -# LLM_MODEL=anthropic/claude-opus-4.6 - -# ============================================================================= -# LLM PROVIDER (NovitaAI) -# ============================================================================= -# NovitaAI — 90+ models, pay-per-use -# Get your key at: https://novita.ai/settings/key-management -# NOVITA_API_KEY= -# NOVITA_BASE_URL=https://api.novita.ai/openai/v1 # Override default base URL - -# ============================================================================= -# LLM PROVIDER (Google AI Studio / Gemini) -# ============================================================================= -# Native Gemini API via Google's OpenAI-compatible endpoint. -# Get your key at: https://aistudio.google.com/app/apikey -# GOOGLE_API_KEY=your_google_ai_studio_key_here -# GEMINI_API_KEY=your_gemini_key_here # alias for GOOGLE_API_KEY -# Optional base URL override (default: Google's OpenAI-compatible endpoint) -# GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai - -# ============================================================================= -# LLM PROVIDER (Ollama Cloud) -# ============================================================================= -# Cloud-hosted open models via Ollama's OpenAI-compatible endpoint. -# Get your key at: https://ollama.com/settings -# OLLAMA_API_KEY=your_ollama_key_here -# Optional base URL override (default: https://ollama.com/v1) -# OLLAMA_BASE_URL=https://ollama.com/v1 - -# ============================================================================= -# LLM PROVIDER (z.ai / GLM) -# ============================================================================= -# z.ai provides access to ZhipuAI GLM models (GLM-4-Plus, etc.) -# Get your key at: https://z.ai or https://open.bigmodel.cn -# GLM_API_KEY= -# GLM_BASE_URL=https://api.z.ai/api/paas/v4 # Override default base URL - -# ============================================================================= -# LLM PROVIDER (Kimi / Moonshot) -# ============================================================================= -# Kimi Code provides access to Moonshot AI coding models (kimi-k2.5, etc.) -# Get your key at: https://platform.kimi.ai (Kimi Code console) -# Keys prefixed sk-kimi- use the Kimi Code API (api.kimi.com) by default. -# Legacy keys from platform.moonshot.ai need KIMI_BASE_URL override below. -# KIMI_API_KEY= -# KIMI_BASE_URL=https://api.kimi.com/coding/v1 # Default for sk-kimi- keys -# KIMI_BASE_URL=https://api.moonshot.ai/v1 # For legacy Moonshot keys -# KIMI_BASE_URL=https://api.moonshot.cn/v1 # For Moonshot China keys -# KIMI_CN_API_KEY= # Dedicated Moonshot China key - -# ============================================================================= -# LLM PROVIDER (Arcee AI) -# ============================================================================= -# Arcee AI provides access to Trinity models (trinity-mini, trinity-large-*) -# Get an Arcee key at: https://chat.arcee.ai/ -# ARCEEAI_API_KEY= -# ARCEE_BASE_URL= # Override default base URL - -# ============================================================================= -# LLM PROVIDER (MiniMax) -# ============================================================================= -# MiniMax provides access to MiniMax models (global endpoint) -# Get your key at: https://www.minimax.io -# MINIMAX_API_KEY= -# MINIMAX_BASE_URL=https://api.minimax.io/v1 # Override default base URL - -# MiniMax China endpoint (for users in mainland China) -# MINIMAX_CN_API_KEY= -# MINIMAX_CN_BASE_URL=https://api.minimaxi.com/v1 # Override default base URL - -# ============================================================================= -# LLM PROVIDER (OpenCode Zen) -# ============================================================================= -# OpenCode Zen provides curated, tested models (GPT, Claude, Gemini, MiniMax, GLM, Kimi) -# Pay-as-you-go pricing. Get your key at: https://opencode.ai/auth -# OPENCODE_ZEN_API_KEY= -# OPENCODE_ZEN_BASE_URL=https://opencode.ai/zen/v1 # Override default base URL - -# ============================================================================= -# LLM PROVIDER (OpenCode Go) -# ============================================================================= -# OpenCode Go provides access to open models (GLM-5, Kimi K2.5, MiniMax M2.5) -# $10/month subscription. Get your key at: https://opencode.ai/auth -# OPENCODE_GO_API_KEY= - -# ============================================================================= -# LLM PROVIDER (Hugging Face Inference Providers) -# ============================================================================= -# Hugging Face routes to 20+ open models via unified OpenAI-compatible endpoint. -# Free tier included ($0.10/month), no markup on provider rates. -# Get your token at: https://huggingface.co/settings/tokens -# Required permission: "Make calls to Inference Providers" -# HF_TOKEN= -# OPENCODE_GO_BASE_URL=https://opencode.ai/zen/go/v1 # Override default base URL - -# ============================================================================= -# LLM PROVIDER (Qwen OAuth) -# ============================================================================= -# Qwen OAuth reuses your local Qwen CLI login (qwen auth qwen-oauth). -# No API key needed — credentials come from ~/.qwen/oauth_creds.json. -# Optional base URL override: -# HERMES_QWEN_BASE_URL=https://portal.qwen.ai/v1 - -# ============================================================================= -# LLM PROVIDER (Xiaomi MiMo) -# ============================================================================= -# Xiaomi MiMo models (mimo-v2-pro, mimo-v2-omni, mimo-v2-flash). -# Get your key at: https://platform.xiaomimimo.com -# XIAOMI_API_KEY=your_key_here -# Optional base URL override: -# XIAOMI_BASE_URL=https://api.xiaomimimo.com/v1 - -# ============================================================================= -# TOOL API KEYS -# ============================================================================= - -# Exa API Key - AI-native web search and contents -# Get at: https://exa.ai -# EXA_API_KEY= - -# Parallel API Key - AI-native web search and extract -# Get at: https://parallel.ai -# PARALLEL_API_KEY= - -# Firecrawl API Key - Web search, extract, and crawl -# Get at: https://firecrawl.dev/ -# FIRECRAWL_API_KEY= - - -# FAL.ai API Key - Image generation -# Get at: https://fal.ai/ -# FAL_KEY= - -# Honcho - Cross-session AI-native user modeling (optional) -# Builds a persistent understanding of the user across sessions and tools. -# Get at: https://app.honcho.dev -# Also requires ~/.honcho/config.json with enabled=true (see README). -# HONCHO_API_KEY= - -# ============================================================================= -# HYPERLIQUID OPTIONAL SKILL -# ============================================================================= -# Optional defaults for the Hyperliquid skill in optional-skills/blockchain/hyperliquid -# -# Hyperliquid API base URL override -# Default: https://api.hyperliquid.xyz -# HYPERLIQUID_API_URL=https://api.hyperliquid-testnet.xyz -# -# Default address for account-level commands like state, fills, orders, and review -# HYPERLIQUID_USER_ADDRESS=0x0000000000000000000000000000000000000000 - -# ============================================================================= -# TERMINAL TOOL CONFIGURATION -# ============================================================================= -# Backend type: "local", "singularity", "docker", "modal", or "ssh" -# Terminal backend is configured in ~/.hermes/config.yaml (terminal.backend). -# Use 'hermes setup' or 'hermes config set terminal.backend docker' to change. -# Supported: local, docker, singularity, modal, ssh -# -# Only override here if you need to force a backend without touching config.yaml: -# TERMINAL_ENV=local - -# Override the container runtime binary (e.g. to use Podman instead of Docker). -# Useful on systems where Docker's storage driver is broken or unavailable. -# HERMES_DOCKER_BINARY=/usr/local/bin/podman - -# Container images (for singularity/docker/modal backends) -# TERMINAL_DOCKER_IMAGE=nikolaik/python-nodejs:python3.11-nodejs20 -# TERMINAL_SINGULARITY_IMAGE=docker://nikolaik/python-nodejs:python3.11-nodejs20 -TERMINAL_MODAL_IMAGE=nikolaik/python-nodejs:python3.11-nodejs20 - - -# Working directory for terminal commands -# For local backend: "." means current directory (resolved automatically) -# For remote backends (ssh/docker/modal/singularity): use an absolute path -# INSIDE the target environment, or leave unset for the backend's default -# (/root for modal, / for docker, ~ for ssh). Do NOT use a host-local path. -# Usually managed by config.yaml (terminal.cwd) — uncomment to override -# TERMINAL_CWD=. - -# Default command timeout in seconds -TERMINAL_TIMEOUT=60 - -# Cleanup inactive environments after this many seconds -TERMINAL_LIFETIME_SECONDS=300 - -# ============================================================================= -# SSH REMOTE EXECUTION (for TERMINAL_ENV=ssh) -# ============================================================================= -# Run terminal commands on a remote server via SSH. -# Agent code stays on your machine, commands execute remotely. -# -# SECURITY BENEFITS: -# - Agent cannot read your .env file (API keys protected) -# - Agent cannot modify its own code -# - Remote server acts as isolated sandbox -# - Can safely configure passwordless sudo on remote -# -# TERMINAL_SSH_HOST=192.168.1.100 -# TERMINAL_SSH_USER=agent -# TERMINAL_SSH_PORT=22 -# TERMINAL_SSH_KEY=~/.ssh/id_rsa - -# ============================================================================= -# SUDO SUPPORT (works with ALL terminal backends) -# ============================================================================= -# If set, enables sudo commands by piping password via `sudo -S`. -# Works with: local, docker, singularity, modal, and ssh backends. -# -# SECURITY WARNING: Password stored in plaintext. Only use on trusted machines. -# -# ALTERNATIVES: -# - For SSH backend: Configure passwordless sudo on the remote server -# - For containers: Run as root inside the container (no sudo needed) -# - For local: Configure /etc/sudoers for specific commands -# - For CLI: Leave unset - you'll be prompted interactively with 45s timeout -# -# SUDO_PASSWORD=your_password_here - -# ============================================================================= -# MODAL CLOUD BACKEND (Optional - for TERMINAL_ENV=modal) -# ============================================================================= -# Modal uses CLI authentication, not environment variables. -# Run: pip install modal && modal setup -# This will authenticate via browser and store credentials locally. -# No API key needed in .env - Modal handles auth automatically. - -# ============================================================================= -# BROWSER TOOL CONFIGURATION (agent-browser + Browserbase) -# ============================================================================= -# Browser automation requires Browserbase cloud service for remote browser execution. -# This allows the agent to navigate websites, fill forms, and extract information. -# -# STEALTH MODES: -# - Basic Stealth: ALWAYS active (random fingerprints, auto CAPTCHA solving) -# - Advanced Stealth: Requires BROWSERBASE_ADVANCED_STEALTH=true (Scale Plan only) - -# Browserbase API Key - Cloud browser execution -# Get at: https://browserbase.com/ -# BROWSERBASE_API_KEY= - -# Browserbase Project ID - From your Browserbase dashboard -# BROWSERBASE_PROJECT_ID= - -# Enable residential proxies for better CAPTCHA solving (default: true) -# Routes traffic through residential IPs, significantly improves success rate -BROWSERBASE_PROXIES=true - -# Enable advanced stealth mode (default: false, requires Scale Plan) -# Uses custom Chromium build to avoid bot detection altogether -BROWSERBASE_ADVANCED_STEALTH=false - -# Browser engine for local mode (default: auto = Chrome) -# "auto" — use Chrome (don't pass --engine flag) -# "lightpanda" — use Lightpanda (1.3-5.8x faster navigation, no screenshots) -# "chrome" — explicitly request Chrome -# Requires agent-browser v0.25.3+. Lightpanda commands that fail or return -# empty results are automatically retried with Chrome. -# Also configurable via browser.engine in config.yaml. -# AGENT_BROWSER_ENGINE=auto - -# Browser session timeout in seconds (default: 300) -# Sessions are cleaned up after this duration of inactivity -BROWSER_SESSION_TIMEOUT=300 - -# Browser inactivity timeout - auto-cleanup inactive sessions (default: 120 = 2 min) -# Browser sessions are automatically closed after this period of no activity -BROWSER_INACTIVITY_TIMEOUT=120 - -# Extra Chromium launch flags passed to agent-browser, comma- or newline-separated. -# Hermes auto-injects "--no-sandbox,--disable-dev-shm-usage" when it detects root -# or AppArmor-restricted unprivileged user namespaces (Ubuntu 23.10+, DGX Spark, -# many container images), so leave this unset unless you need extra flags. -# Setting this disables the auto-injection. -# AGENT_BROWSER_ARGS=--no-sandbox - -# Camofox local anti-detection browser (Camoufox-based Firefox). -# Set CAMOFOX_URL to route the browser tools through a local Camofox server -# instead of agent-browser/Browserbase. See docs/user-guide/features/browser.md. -# CAMOFOX_URL=http://localhost:9377 - -# Externally managed Camofox sessions — when another app owns the visible -# Camofox browser, set these so Hermes shares the same userId/profile instead -# of creating its own isolated session. -# CAMOFOX_USER_ID= -# CAMOFOX_SESSION_KEY= -# Set to true to reuse an already-open Camofox tab for this identity before -# creating a new one (useful for gateway restarts). -# CAMOFOX_ADOPT_EXISTING_TAB=false - -# ============================================================================= -# SESSION LOGGING -# ============================================================================= -# Session trajectories are automatically saved to logs/ directory -# Format: logs/session_YYYYMMDD_HHMMSS_UUID.json -# Contains full conversation history in trajectory format for debugging/replay - -# ============================================================================= -# VOICE TRANSCRIPTION & OPENAI TTS -# ============================================================================= -# Required for voice message transcription (Whisper) and OpenAI TTS voices. -# Uses OpenAI's API directly (not via OpenRouter). -# Named VOICE_TOOLS_OPENAI_KEY to avoid interference with OpenRouter. -# Get at: https://platform.openai.com/api-keys -# VOICE_TOOLS_OPENAI_KEY= - -# ============================================================================= -# SLACK INTEGRATION -# ============================================================================= -# Slack Bot Token - From Slack App settings (OAuth & Permissions) -# Get at: https://api.slack.com/apps -# SLACK_BOT_TOKEN=xoxb-... - -# Slack App Token - For Socket Mode (App-Level Tokens in Slack App settings) -# SLACK_APP_TOKEN=xapp-... - -# Slack allowed users (comma-separated Slack user IDs) -# SLACK_ALLOWED_USERS= - -# ============================================================================= -# TELEGRAM INTEGRATION -# ============================================================================= -# Telegram Bot Token - From @BotFather (https://t.me/BotFather) -# TELEGRAM_BOT_TOKEN= -# TELEGRAM_ALLOWED_USERS= # Comma-separated user IDs -# TELEGRAM_HOME_CHANNEL= # Default chat for cron delivery -# TELEGRAM_HOME_CHANNEL_NAME= # Display name for home channel - -# Webhook mode (optional — for cloud deployments like Fly.io/Railway) -# Default is long polling. Setting TELEGRAM_WEBHOOK_URL switches to webhook mode. -# TELEGRAM_WEBHOOK_URL=https://my-app.fly.dev/telegram -# TELEGRAM_WEBHOOK_PORT=8443 -# TELEGRAM_WEBHOOK_SECRET= # Recommended for production - -# WhatsApp (built-in Baileys bridge — run `hermes whatsapp` to pair) -# WHATSAPP_ENABLED=false -# WHATSAPP_ALLOWED_USERS=15551234567 - -# Email (IMAP/SMTP — send and receive emails as Hermes) -# For Gmail: enable 2FA → create App Password at https://myaccount.google.com/apppasswords -# EMAIL_ADDRESS=hermes@gmail.com -# EMAIL_PASSWORD=xxxx xxxx xxxx xxxx -# EMAIL_IMAP_HOST=imap.gmail.com -# EMAIL_IMAP_PORT=993 -# EMAIL_SMTP_HOST=smtp.gmail.com -# EMAIL_SMTP_PORT=587 -# EMAIL_POLL_INTERVAL=15 -# EMAIL_ALLOWED_USERS=your@email.com -# EMAIL_HOME_ADDRESS=your@email.com - -# Gateway-wide: allow ALL users without an allowlist (default: false = deny) -# Only set to true if you intentionally want open access. -# GATEWAY_ALLOW_ALL_USERS=false - -# ============================================================================= -# RESPONSE PACING -# ============================================================================= -# Human-like delays between message chunks on messaging platforms. -# Makes the bot feel less robotic. -# HERMES_HUMAN_DELAY_MODE=off # off | natural | custom -# HERMES_HUMAN_DELAY_MIN_MS=800 # Min delay in ms (custom mode) -# HERMES_HUMAN_DELAY_MAX_MS=2500 # Max delay in ms (custom mode) - -# ============================================================================= -# DEBUG OPTIONS -# ============================================================================= -WEB_TOOLS_DEBUG=false -VISION_TOOLS_DEBUG=false -MOA_TOOLS_DEBUG=false -IMAGE_TOOLS_DEBUG=false - -# ============================================================================= -# CONTEXT COMPRESSION (Auto-shrinks long conversations) -# ============================================================================= -# When conversation approaches model's context limit, middle turns are -# automatically summarized to free up space. -# -# Context compression is configured in ~/.hermes/config.yaml under compression: -# CONTEXT_COMPRESSION_ENABLED=true # Enable auto-compression (default: true) -# CONTEXT_COMPRESSION_THRESHOLD=0.85 # Compress at 85% of context limit -# Model is set via compression.summary_model in config.yaml (default: google/gemini-3-flash-preview) - -# ============================================================================= -# SKILLS HUB (GitHub integration for skill search/install/publish) -# ============================================================================= - -# GitHub Personal Access Token — for higher API rate limits on skill search/install -# Get at: https://github.com/settings/tokens (Fine-grained recommended) -# GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx - -# GitHub App credentials (optional — for bot identity on PRs) -# GITHUB_APP_ID= -# GITHUB_APP_PRIVATE_KEY_PATH= -# GITHUB_APP_INSTALLATION_ID= - -# Groq API key (free tier — used for Whisper STT in voice mode) -# GROQ_API_KEY= - -# ============================================================================= -# STT PROVIDER SELECTION -# ============================================================================= -# Default STT provider is "local" (faster-whisper) — runs on your machine, no API key needed. -# Install with: pip install faster-whisper -# Model downloads automatically on first use (~150 MB for "base"). -# To use cloud providers instead, set GROQ_API_KEY or VOICE_TOOLS_OPENAI_KEY above. -# Provider priority: local > groq > openai -# Configure in config.yaml: stt.provider: local | groq | openai - -# ============================================================================= -# STT ADVANCED OVERRIDES (optional) -# ============================================================================= -# Override default STT models per provider (normally set via stt.model in config.yaml) -# STT_GROQ_MODEL=whisper-large-v3-turbo -# STT_OPENAI_MODEL=whisper-1 - -# Override STT provider endpoints (for proxies or self-hosted instances) -# GROQ_BASE_URL=https://api.groq.com/openai/v1 -# STT_OPENAI_BASE_URL=https://api.openai.com/v1 - -# ============================================================================= -# MICROSOFT TEAMS INTEGRATION -# ============================================================================= -# Register a Bot in Azure: https://dev.botframework.com/ → "Register a bot" -# Or use Azure Portal: Azure Active Directory → App registrations → New registration -# Then add the bot to Teams via the Bot Framework or App Studio. -# -# TEAMS_CLIENT_ID= # Azure AD App (client) ID -# TEAMS_CLIENT_SECRET= # Azure AD client secret value -# TEAMS_TENANT_ID= # Azure AD tenant ID (or "common" for multi-tenant) -# TEAMS_ALLOWED_USERS= # Comma-separated AAD object IDs or UPNs -# TEAMS_ALLOW_ALL_USERS=false # Set true to skip the allowlist -# TEAMS_HOME_CHANNEL= # Default channel/chat ID for cron delivery -# TEAMS_HOME_CHANNEL_NAME= # Display name for the home channel -# TEAMS_PORT=3978 # Webhook listen port (Bot Framework default) - -# ============================================================================= -# GOOGLE CHAT INTEGRATION -# ============================================================================= -# Connects via Cloud Pub/Sub pull subscription (no public URL required). -# Setup walkthrough: website/docs/user-guide/messaging/google_chat.md. -# 1. Create a GCP project, enable the Google Chat API and Cloud Pub/Sub. -# 2. Create a Service Account with roles/pubsub.subscriber on the -# subscription (NOT project-wide); download the JSON key. -# 3. Configure your Chat app at console.cloud.google.com/apis/credentials -# → Google Chat API → Configuration → Cloud Pub/Sub topic. -# 4. (Optional, for native attachment delivery) Each user runs -# `/setup-files` once in their own DM after Pub/Sub is wired up. -# -# GOOGLE_CHAT_PROJECT_ID= # GCP project hosting the topic (or set GOOGLE_CLOUD_PROJECT) -# GOOGLE_CHAT_SUBSCRIPTION_NAME= # Full path: projects//subscriptions/ -# GOOGLE_CHAT_SERVICE_ACCOUNT_JSON= # Path to SA JSON (or set GOOGLE_APPLICATION_CREDENTIALS) -# GOOGLE_CHAT_ALLOWED_USERS= # Comma-separated emails allowed to talk to the bot -# GOOGLE_CHAT_ALLOW_ALL_USERS=false # Set true to skip the allowlist -# GOOGLE_CHAT_HOME_CHANNEL= # Default space (spaces/XXXX) for cron delivery -# GOOGLE_CHAT_HOME_CHANNEL_NAME= # Display name for the home channel diff --git a/reference/hermes-agent-2026.5.16/.envrc b/reference/hermes-agent-2026.5.16/.envrc deleted file mode 100755 index 45c5952..0000000 --- a/reference/hermes-agent-2026.5.16/.envrc +++ /dev/null @@ -1,5 +0,0 @@ -watch_file pyproject.toml uv.lock -watch_file ui-tui/package-lock.json ui-tui/package.json -watch_file flake.nix flake.lock nix/devShell.nix nix/tui.nix nix/package.nix nix/python.nix - -use flake diff --git a/reference/hermes-agent-2026.5.16/.gitattributes b/reference/hermes-agent-2026.5.16/.gitattributes deleted file mode 100755 index 8726216..0000000 --- a/reference/hermes-agent-2026.5.16/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto-generated files — collapse diffs and exclude from language stats -web/package-lock.json linguist-generated=true diff --git a/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/bug_report.yml b/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100755 index 67a3f64..0000000 --- a/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,162 +0,0 @@ -name: "🐛 Bug Report" -description: Report a bug — something that's broken, crashes, or behaves incorrectly. -title: "[Bug]: " -labels: ["bug"] -body: - - type: markdown - attributes: - value: | - Thanks for reporting a bug! Please fill out the sections below so we can reproduce and fix it quickly. - - **Before submitting**, please: - - [ ] Search [existing issues](https://github.com/NousResearch/hermes-agent/issues) to avoid duplicates - - [ ] Update to the latest version (`hermes update`) and confirm the bug still exists - - [ ] Run `hermes debug share` and paste the links below (see Debug Report section) - - - type: textarea - id: description - attributes: - label: Bug Description - description: A clear description of what's broken. Include error messages, tracebacks, or screenshots if relevant. - placeholder: | - What happened? What did you expect to happen instead? - validations: - required: true - - - type: textarea - id: reproduction - attributes: - label: Steps to Reproduce - description: Minimal steps to trigger the bug. The more specific, the faster we can fix it. - placeholder: | - 1. Run `hermes chat` - 2. Send the message "..." - 3. Agent calls tool X - 4. Error appears: ... - validations: - required: true - - - type: textarea - id: expected - attributes: - label: Expected Behavior - description: What should have happened instead? - validations: - required: true - - - type: textarea - id: actual - attributes: - label: Actual Behavior - description: What actually happened? Include full error output if available. - validations: - required: true - - - type: dropdown - id: component - attributes: - label: Affected Component - description: Which part of Hermes is affected? - multiple: true - options: - - CLI (interactive chat) - - Gateway (Telegram/Discord/Slack/WhatsApp) - - Setup / Installation - - Tools (terminal, file ops, web, code execution, etc.) - - Skills (skill loading, skill hub, skill guard) - - Agent Core (conversation loop, context compression, memory) - - Configuration (config.yaml, .env, hermes setup) - - Other - validations: - required: true - - - type: dropdown - id: platform - attributes: - label: Messaging Platform (if gateway-related) - description: Which platform adapter is affected? - multiple: true - options: - - N/A (CLI only) - - Telegram - - Discord - - Slack - - WhatsApp - - - type: textarea - id: debug-report - attributes: - label: Debug Report - description: | - Run `hermes debug share` from your terminal and paste the links it prints here. - This uploads your system info, config, and recent logs to a paste service automatically. - - If you're in an interactive chat session, you can also use the `/debug` slash command — it does the same thing. - - If the upload fails, run `hermes debug share --local` and paste the output directly. - placeholder: | - Report https://paste.rs/abc123 - agent.log https://paste.rs/def456 - gateway.log https://paste.rs/ghi789 - render: shell - validations: - required: true - - - type: input - id: os - attributes: - label: Operating System - description: e.g. Ubuntu 24.04, macOS 15.2, Windows 11 - placeholder: Ubuntu 24.04 - validations: - required: true - - - type: input - id: python-version - attributes: - label: Python Version - description: Output of `python --version` - placeholder: "3.11.9" - - - type: input - id: hermes-version - attributes: - label: Hermes Version - description: Output of `hermes version` - placeholder: "2.1.0" - - - type: textarea - id: logs - attributes: - label: Additional Logs / Traceback (optional) - description: | - The debug report above covers most logs. Use this field for any extra error output, - tracebacks, or screenshots not captured by `hermes debug share`. - render: shell - - - type: textarea - id: root-cause - attributes: - label: Root Cause Analysis (optional) - description: | - If you've dug into the code and identified the root cause, share it here. - Include file paths, line numbers, and code snippets if possible. This massively speeds up fixes. - placeholder: | - The bug is in `gateway/run.py` line 949. `len(history)` counts session_meta entries - but `agent_messages` was built from filtered history... - - - type: textarea - id: proposed-fix - attributes: - label: Proposed Fix (optional) - description: If you have a fix in mind (or a PR ready), describe it here. - placeholder: | - Replace `.get()` with `.pop()` on line 289 of `gateway/platforms/base.py` - to actually clear the pending message after retrieval. - - - type: checkboxes - id: pr-ready - attributes: - label: Are you willing to submit a PR for this? - options: - - label: I'd like to fix this myself and submit a PR diff --git a/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/config.yml b/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100755 index 0daa52c..0000000 --- a/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,11 +0,0 @@ -blank_issues_enabled: true -contact_links: - - name: 💬 Nous Research Discord - url: https://discord.gg/NousResearch - about: For quick questions, showcasing projects, sharing skills, and community chat. - - name: 📖 Documentation - url: https://github.com/NousResearch/hermes-agent/blob/main/README.md - about: Check the README and docs before opening an issue. - - name: 🤝 Contributing Guide - url: https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md - about: Read this before submitting a PR. diff --git a/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/feature_request.yml b/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100755 index 720cc8f..0000000 --- a/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: "✨ Feature Request" -description: Suggest a new feature or improvement. -title: "[Feature]: " -labels: ["enhancement"] -body: - - type: markdown - attributes: - value: | - Thanks for the suggestion! Before submitting, please consider: - - - **Is this a new skill?** Most capabilities should be [skills, not tools](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#should-it-be-a-skill-or-a-tool). If it's a specialized integration (crypto, NFT, niche SaaS), it belongs on the Skills Hub, not bundled. - - **Search [existing issues](https://github.com/NousResearch/hermes-agent/issues)** — someone may have already proposed this. - - - type: textarea - id: problem - attributes: - label: Problem or Use Case - description: What problem does this solve? What are you trying to do that you can't today? - placeholder: | - I'm trying to use Hermes with [provider/platform/workflow] but currently - there's no way to... - validations: - required: true - - - type: textarea - id: solution - attributes: - label: Proposed Solution - description: How do you think this should work? Be as specific as you can — CLI flags, config options, UI behavior. - placeholder: | - Add a `--foo` flag to `hermes chat` that enables... - Or: Add a config key `bar.baz` that controls... - validations: - required: true - - - type: textarea - id: alternatives - attributes: - label: Alternatives Considered - description: What other approaches did you consider? Why is the proposed solution better? - - - type: dropdown - id: type - attributes: - label: Feature Type - options: - - New tool - - New bundled skill - - CLI improvement - - Gateway / messaging improvement - - Configuration option - - Performance / reliability - - Developer experience (tests, docs, CI) - - Other - validations: - required: true - - - type: dropdown - id: scope - attributes: - label: Scope - description: How big is this change? - options: - - Small (single file, < 50 lines) - - Medium (few files, < 300 lines) - - Large (new module or significant refactor) - - - type: checkboxes - id: pr-ready - attributes: - label: Contribution - options: - - label: I'd like to implement this myself and submit a PR - - - type: textarea - id: debug-report - attributes: - label: Debug Report (optional) - description: | - If this feature request is related to a problem you're experiencing, run `hermes debug share` and paste the links here. - In an interactive chat session, you can use `/debug` instead. - This helps us understand your environment and any related logs. - placeholder: | - Report https://paste.rs/abc123 - render: shell diff --git a/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/setup_help.yml b/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/setup_help.yml deleted file mode 100755 index 974181b..0000000 --- a/reference/hermes-agent-2026.5.16/.github/ISSUE_TEMPLATE/setup_help.yml +++ /dev/null @@ -1,112 +0,0 @@ -name: "🔧 Setup / Installation Help" -description: Having trouble installing or configuring Hermes? Ask here. -title: "[Setup]: " -labels: ["setup"] -body: - - type: markdown - attributes: - value: | - Sorry you're having trouble! Please fill out the details below so we can help. - - **Quick checks first:** - - Run `hermes debug share` and paste the links in the Debug Report section below - - If you're in a chat session, you can use `/debug` instead — it does the same thing - - Try `hermes update` to get the latest version - - Check the [README troubleshooting section](https://github.com/NousResearch/hermes-agent#troubleshooting) - - For general questions, consider the [Nous Research Discord](https://discord.gg/NousResearch) for faster help - - - type: textarea - id: description - attributes: - label: What's Going Wrong? - description: Describe what you're trying to do and where it fails. - placeholder: | - I ran `hermes setup` and selected Nous Portal, but when I try to - start the gateway I get... - validations: - required: true - - - type: textarea - id: steps - attributes: - label: Steps Taken - description: What did you do? Include the exact commands you ran. - placeholder: | - 1. Ran the install script: `curl -fsSL ... | bash` - 2. Ran `hermes setup` and chose "Quick setup" - 3. Selected OpenRouter, entered API key - 4. Ran `hermes chat` and got error... - validations: - required: true - - - type: dropdown - id: install-method - attributes: - label: Installation Method - options: - - Install script (curl | bash) - - Manual clone + pip/uv install - - PowerShell installer (Windows) - - Docker - - Other - validations: - required: true - - - type: input - id: os - attributes: - label: Operating System - placeholder: Ubuntu 24.04 / macOS 15.2 / Windows 11 - validations: - required: true - - - type: input - id: python-version - attributes: - label: Python Version - description: Output of `python --version` (or `python3 --version`) - placeholder: "3.11.9" - - - type: input - id: hermes-version - attributes: - label: Hermes Version - description: Output of `hermes version` (if install got that far) - placeholder: "2.1.0" - - - type: textarea - id: debug-report - attributes: - label: Debug Report - description: | - Run `hermes debug share` from your terminal and paste the links it prints here. - This uploads your system info, config, and recent logs to a paste service automatically. - - If you're in an interactive chat session, you can also use the `/debug` slash command — it does the same thing. - - If the upload fails or install didn't get that far, run `hermes debug share --local` and paste the output directly. - If even that doesn't work, run `hermes doctor` and paste that output instead. - placeholder: | - Report https://paste.rs/abc123 - agent.log https://paste.rs/def456 - gateway.log https://paste.rs/ghi789 - render: shell - - - type: textarea - id: error-output - attributes: - label: Full Error Output - description: Paste the complete error message or traceback. This will be auto-formatted. - render: shell - validations: - required: true - - - type: textarea - id: tried - attributes: - label: What I've Already Tried - description: List any fixes or workarounds you've already attempted. - placeholder: | - - Ran `hermes update` - - Tried reinstalling with `pip install -e ".[all]"` - - Checked that OPENROUTER_API_KEY is set in ~/.hermes/.env diff --git a/reference/hermes-agent-2026.5.16/.github/PULL_REQUEST_TEMPLATE.md b/reference/hermes-agent-2026.5.16/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100755 index 5496eb5..0000000 --- a/reference/hermes-agent-2026.5.16/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,75 +0,0 @@ -## What does this PR do? - - - - - -## Related Issue - - - -Fixes # - -## Type of Change - - - -- [ ] 🐛 Bug fix (non-breaking change that fixes an issue) -- [ ] ✨ New feature (non-breaking change that adds functionality) -- [ ] 🔒 Security fix -- [ ] 📝 Documentation update -- [ ] ✅ Tests (adding or improving test coverage) -- [ ] ♻️ Refactor (no behavior change) -- [ ] 🎯 New skill (bundled or hub) - -## Changes Made - - - -- - -## How to Test - - - -1. -2. -3. - -## Checklist - - - -### Code - -- [ ] I've read the [Contributing Guide](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md) -- [ ] My commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) (`fix(scope):`, `feat(scope):`, etc.) -- [ ] I searched for [existing PRs](https://github.com/NousResearch/hermes-agent/pulls) to make sure this isn't a duplicate -- [ ] My PR contains **only** changes related to this fix/feature (no unrelated commits) -- [ ] I've run `pytest tests/ -q` and all tests pass -- [ ] I've added tests for my changes (required for bug fixes, strongly encouraged for features) -- [ ] I've tested on my platform: - -### Documentation & Housekeeping - - - -- [ ] I've updated relevant documentation (README, `docs/`, docstrings) — or N/A -- [ ] I've updated `cli-config.yaml.example` if I added/changed config keys — or N/A -- [ ] I've updated `CONTRIBUTING.md` or `AGENTS.md` if I changed architecture or workflows — or N/A -- [ ] I've considered cross-platform impact (Windows, macOS) per the [compatibility guide](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#cross-platform-compatibility) — or N/A -- [ ] I've updated tool descriptions/schemas if I changed tool behavior — or N/A - -## For New Skills - - - -- [ ] This skill is **broadly useful** to most users (if bundled) — see [Contributing Guide](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#should-the-skill-be-bundled) -- [ ] SKILL.md follows the [standard format](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#skillmd-format) (frontmatter, trigger conditions, steps, pitfalls) -- [ ] No external dependencies that aren't already available (prefer stdlib, curl, existing Hermes tools) -- [ ] I've tested the skill end-to-end: `hermes --toolsets skills -q "Use the X skill to do Y"` - -## Screenshots / Logs - - - diff --git a/reference/hermes-agent-2026.5.16/.github/actions/hermes-smoke-test/action.yml b/reference/hermes-agent-2026.5.16/.github/actions/hermes-smoke-test/action.yml deleted file mode 100755 index 08b9f93..0000000 --- a/reference/hermes-agent-2026.5.16/.github/actions/hermes-smoke-test/action.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Hermes smoke test -description: > - Run the image's built-in entrypoint against `--help` and `dashboard --help` - to catch basic runtime regressions before publishing. Requires the image - to already be loaded into the local Docker daemon under `image`. - - Works identically on amd64 and arm64 runners. - -inputs: - image: - description: Fully-qualified image tag (e.g. nousresearch/hermes-agent:test) - required: true - -runs: - using: composite - steps: - - name: Ensure /tmp/hermes-test is hermes-writable - shell: bash - run: | - # The image runs as the hermes user (UID 10000). GitHub Actions - # creates /tmp/hermes-test root-owned by default, which hermes - # can't write to — chown it to match the in-container UID before - # bind-mounting. Real users doing `docker run -v ~/.hermes:...` - # with their own UID hit the same issue and have their own - # remediations (HERMES_UID env var, or chown locally). - mkdir -p /tmp/hermes-test - sudo chown -R 10000:10000 /tmp/hermes-test - - - name: hermes --help - shell: bash - run: | - docker run --rm \ - -v /tmp/hermes-test:/opt/data \ - --entrypoint /opt/hermes/docker/entrypoint.sh \ - "${{ inputs.image }}" --help - - - name: hermes dashboard --help - shell: bash - run: | - # Regression guard for #9153: dashboard was present in source but - # missing from the published image. If this fails, something in - # the Dockerfile is excluding the dashboard subcommand from the - # installed package. - docker run --rm \ - -v /tmp/hermes-test:/opt/data \ - --entrypoint /opt/hermes/docker/entrypoint.sh \ - "${{ inputs.image }}" dashboard --help diff --git a/reference/hermes-agent-2026.5.16/.github/actions/nix-setup/action.yml b/reference/hermes-agent-2026.5.16/.github/actions/nix-setup/action.yml deleted file mode 100755 index 0aeaf91..0000000 --- a/reference/hermes-agent-2026.5.16/.github/actions/nix-setup/action.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: 'Setup Nix' -description: 'Install Nix and configure Cachix binary cache' - -inputs: - cachix-auth-token: - description: 'Cachix auth token (enables push). Omit for read-only.' - required: false - default: '' - -runs: - using: composite - steps: - - uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # v22 - - uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17 - with: - name: hermes-agent - authToken: ${{ inputs.cachix-auth-token }} - continue-on-error: true diff --git a/reference/hermes-agent-2026.5.16/.github/dependabot.yml b/reference/hermes-agent-2026.5.16/.github/dependabot.yml deleted file mode 100755 index 3854c8f..0000000 --- a/reference/hermes-agent-2026.5.16/.github/dependabot.yml +++ /dev/null @@ -1,44 +0,0 @@ -# Dependabot configuration for hermes-agent. -# -# Deliberately scoped to github-actions only. -# -# We do NOT enable Dependabot for pip / npm / any source-dependency ecosystem -# because we pin source dependencies exactly (uv.lock, package-lock.json) as -# part of our supply-chain posture. Automatic version-bump PRs against those -# pins would undermine the strategy — pins are moved deliberately, after -# review, not on a schedule. -# -# github-actions is the exception: action pins (we use full commit SHAs per -# supply-chain policy) must be updated when upstream actions publish -# patches — usually themselves security fixes. Dependabot opens a PR with -# the new SHA and release notes; we review and merge like any other PR. -# -# Security-update PRs for source dependencies (opened ONLY when a CVE is -# published affecting a currently-pinned version) are enabled separately -# via the repo's Dependabot security updates setting -# (Settings → Code security → Dependabot → Dependabot security updates). -# Those are CVE-only, not schedule-driven, and do not conflict with our -# pinning strategy — they fire when a pinned version becomes known-bad, -# which is exactly when we want to move the pin. - -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - day: "monday" - open-pull-requests-limit: 5 - labels: - - "dependencies" - - "github-actions" - commit-message: - prefix: "chore(actions)" - include: "scope" - groups: - # Batch routine action bumps into one PR per week to reduce noise. - # Security updates still open individually and bypass grouping. - actions-minor-patch: - update-types: - - "minor" - - "patch" diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/contributor-check.yml b/reference/hermes-agent-2026.5.16/.github/workflows/contributor-check.yml deleted file mode 100755 index 3ca4991..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/contributor-check.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Contributor Attribution Check - -on: - pull_request: - branches: [main] - paths: - # Only run when code files change (not docs-only PRs) - - '*.py' - - '**/*.py' - - '.github/workflows/contributor-check.yml' - -permissions: - contents: read - -jobs: - check-attribution: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - fetch-depth: 0 # Full history needed for git log - - - name: Check for unmapped contributor emails - run: | - # Get the merge base between this PR and main - MERGE_BASE=$(git merge-base origin/main HEAD) - - # Find any new author emails in this PR's commits - NEW_EMAILS=$(git log ${MERGE_BASE}..HEAD --format='%ae' --no-merges | sort -u) - - if [ -z "$NEW_EMAILS" ]; then - echo "No new commits to check." - exit 0 - fi - - # Check each email against AUTHOR_MAP in release.py - MISSING="" - while IFS= read -r email; do - # Skip teknium and bot emails - case "$email" in - *teknium*|*noreply@github.com*|*dependabot*|*github-actions*|*anthropic.com*|*cursor.com*) - continue ;; - esac - - # Check if email is in AUTHOR_MAP (either as a key or matches noreply pattern) - if echo "$email" | grep -qP '\+.*@users\.noreply\.github\.com'; then - continue # GitHub noreply emails auto-resolve - fi - - if ! grep -qF "\"${email}\"" scripts/release.py 2>/dev/null; then - AUTHOR=$(git log --author="$email" --format='%an' -1) - MISSING="${MISSING}\n ${email} (${AUTHOR})" - fi - done <<< "$NEW_EMAILS" - - if [ -n "$MISSING" ]; then - echo "" - echo "⚠️ New contributor email(s) not in AUTHOR_MAP:" - echo -e "$MISSING" - echo "" - echo "Please add mappings to scripts/release.py AUTHOR_MAP:" - echo -e "$MISSING" | while read -r line; do - email=$(echo "$line" | sed 's/^ *//' | cut -d' ' -f1) - [ -z "$email" ] && continue - echo " \"${email}\": \"\"," - done - echo "" - echo "To find the GitHub username for an email:" - echo " gh api 'search/users?q=EMAIL+in:email' --jq '.items[0].login'" - exit 1 - else - echo "✅ All contributor emails are mapped in AUTHOR_MAP." - fi diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/deploy-site.yml b/reference/hermes-agent-2026.5.16/.github/workflows/deploy-site.yml deleted file mode 100755 index 8df74c0..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/deploy-site.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: Deploy Site - -on: - release: - types: [published] - push: - branches: [main] - paths: - - 'website/**' - - 'skills/**' - - 'optional-skills/**' - - '.github/workflows/deploy-site.yml' - workflow_dispatch: - -permissions: - pages: write - id-token: write - -concurrency: - group: pages - cancel-in-progress: false - -jobs: - deploy-vercel: - if: github.event_name == 'release' - runs-on: ubuntu-latest - steps: - - name: Trigger Vercel Deploy - run: curl -X POST "${{ secrets.VERCEL_DEPLOY_HOOK }}" - - deploy-docs: - if: github.repository == 'NousResearch/hermes-agent' - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deploy.outputs.page_url }} - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 - with: - node-version: 20 - cache: npm - cache-dependency-path: website/package-lock.json - - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version: '3.11' - - - name: Install PyYAML for skill extraction - run: pip install pyyaml==6.0.2 httpx==0.28.1 - - - name: Extract skill metadata for dashboard - run: python3 website/scripts/extract-skills.py - - - name: Regenerate per-skill docs pages + catalogs - run: python3 website/scripts/generate-skill-docs.py - - - name: Build skills index (if not already present) - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if [ ! -f website/static/api/skills-index.json ]; then - python3 scripts/build_skills_index.py || echo "Skills index build failed (non-fatal)" - fi - - - name: Install dependencies - run: npm ci - working-directory: website - - - name: Build Docusaurus - run: npm run build - working-directory: website - - - name: Stage deployment - run: | - mkdir -p _site/docs - cp -r website/build/* _site/docs/ - # llms.txt / llms-full.txt are also published at the site root - # (https://hermes-agent.nousresearch.com/llms.txt) because some - # agents and IDE plugins probe the classic root-level path rather - # than /docs/llms.txt. Same file, two URLs, one source of truth. - if [ -f website/build/llms.txt ]; then - cp website/build/llms.txt _site/llms.txt - fi - if [ -f website/build/llms-full.txt ]; then - cp website/build/llms-full.txt _site/llms-full.txt - fi - - - name: Upload artifact - uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 - with: - path: _site - - - name: Deploy to GitHub Pages - id: deploy - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/docker-publish.yml b/reference/hermes-agent-2026.5.16/.github/workflows/docker-publish.yml deleted file mode 100755 index cccb8f3..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/docker-publish.yml +++ /dev/null @@ -1,534 +0,0 @@ -name: Docker Build and Publish - -on: - push: - branches: [main] - paths: - - '**/*.py' - - 'pyproject.toml' - - 'uv.lock' - - 'Dockerfile' - - 'docker/**' - - '.github/workflows/docker-publish.yml' - - '.github/actions/hermes-smoke-test/**' - pull_request: - branches: [main] - paths: - - '**/*.py' - - 'pyproject.toml' - - 'uv.lock' - - 'Dockerfile' - - 'docker/**' - - '.github/workflows/docker-publish.yml' - - '.github/actions/hermes-smoke-test/**' - release: - types: [published] - -permissions: - contents: read - -# Concurrency: push/release runs are NEVER cancelled so every merge gets its -# own SHA-tagged image; :main and :latest are guarded separately by the -# move-main and move-latest jobs. PR runs reuse a PR-scoped group with -# cancel-in-progress: true so rapid pushes to the same PR collapse to the -# latest commit. -concurrency: - group: docker-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -env: - IMAGE_NAME: nousresearch/hermes-agent - -jobs: - # --------------------------------------------------------------------------- - # Build amd64 natively. This job also runs the smoke tests (basic --help - # and the dashboard subcommand regression guard from #9153), because amd64 - # is the only arch we can `load` into the local daemon on an amd64 runner. - # --------------------------------------------------------------------------- - build-amd64: - # Only run on the upstream repository, not on forks - if: github.repository == 'NousResearch/hermes-agent' - runs-on: ubuntu-latest - timeout-minutes: 45 - outputs: - digest: ${{ steps.push.outputs.digest }} - steps: - - name: Checkout code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - submodules: recursive - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - - # Build once, load into the local daemon for smoke testing. Cached - # to gha with a per-arch scope; the push step below reuses every - # layer from this build. - - name: Build image (amd64, smoke test) - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 - with: - context: . - file: Dockerfile - load: true - platforms: linux/amd64 - tags: ${{ env.IMAGE_NAME }}:test - cache-from: type=gha,scope=docker-amd64 - cache-to: type=gha,mode=max,scope=docker-amd64 - - - name: Smoke test image - uses: ./.github/actions/hermes-smoke-test - with: - image: ${{ env.IMAGE_NAME }}:test - - - name: Log in to Docker Hub - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'release' - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # Push amd64 by digest only (no tag). The merge job assembles the - # tagged manifest list. `push-by-digest=true` is docker's recommended - # pattern for multi-runner multi-platform builds. - # - # We apply the OCI revision label here (and again on arm64) because - # the move-main / move-latest jobs read it off the linux/amd64 - # sub-manifest config of the floating tag to decide whether it's safe - # to advance. The label must be on each per-arch image — manifest - # lists themselves don't carry image config labels. - - name: Push amd64 by digest - id: push - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'release' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 - with: - context: . - file: Dockerfile - platforms: linux/amd64 - labels: | - org.opencontainers.image.revision=${{ github.sha }} - outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true - cache-from: type=gha,scope=docker-amd64 - cache-to: type=gha,mode=max,scope=docker-amd64 - - # Write the digest to a file and upload it as an artifact so the - # merge job can stitch both per-arch digests into a manifest list. - - name: Export digest - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'release' - run: | - mkdir -p /tmp/digests - digest="${{ steps.push.outputs.digest }}" - touch "/tmp/digests/${digest#sha256:}" - - - name: Upload digest artifact - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'release' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: digest-amd64 - path: /tmp/digests/* - if-no-files-found: error - retention-days: 1 - - # --------------------------------------------------------------------------- - # Build arm64 natively on GitHub's free arm64 runner. This replaces the - # previous QEMU-emulated arm64 build, which was ~5-10x slower and shared - # a cache scope with amd64. Matches the amd64 job's shape: build+load, - # smoke test, then on push/release push by digest. - # --------------------------------------------------------------------------- - build-arm64: - if: github.repository == 'NousResearch/hermes-agent' - runs-on: ubuntu-24.04-arm - timeout-minutes: 45 - outputs: - digest: ${{ steps.push.outputs.digest }} - steps: - - name: Checkout code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - submodules: recursive - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - - # Build once, load into the local daemon for smoke testing. Cached - # to gha with a per-arch scope; the push step below reuses every - # layer from this build. - - name: Build image (arm64, smoke test) - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 - with: - context: . - file: Dockerfile - load: true - platforms: linux/arm64 - tags: ${{ env.IMAGE_NAME }}:test - cache-from: type=gha,scope=docker-arm64 - cache-to: type=gha,mode=max,scope=docker-arm64 - - - name: Smoke test image - uses: ./.github/actions/hermes-smoke-test - with: - image: ${{ env.IMAGE_NAME }}:test - - - name: Log in to Docker Hub - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'release' - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Push arm64 by digest - id: push - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'release' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 - with: - context: . - file: Dockerfile - platforms: linux/arm64 - labels: | - org.opencontainers.image.revision=${{ github.sha }} - outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true - cache-from: type=gha,scope=docker-arm64 - cache-to: type=gha,mode=max,scope=docker-arm64 - - - name: Export digest - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'release' - run: | - mkdir -p /tmp/digests - digest="${{ steps.push.outputs.digest }}" - touch "/tmp/digests/${digest#sha256:}" - - - name: Upload digest artifact - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'release' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: digest-arm64 - path: /tmp/digests/* - if-no-files-found: error - retention-days: 1 - - # --------------------------------------------------------------------------- - # Stitch both per-arch digests into a single tagged multi-arch manifest. - # This is a registry-side operation — no building, no layer re-push — - # so it runs in ~30 seconds. On main pushes it produces :sha-. - # On releases it produces :. - # --------------------------------------------------------------------------- - merge: - if: github.repository == 'NousResearch/hermes-agent' && (github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'release') - runs-on: ubuntu-latest - needs: [build-amd64, build-arm64] - timeout-minutes: 10 - outputs: - pushed_sha_tag: ${{ steps.mark_pushed.outputs.pushed }} - pushed_release_tag: ${{ steps.mark_release_pushed.outputs.pushed }} - release_tag: ${{ steps.tag.outputs.tag }} - steps: - - name: Download digests - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - path: /tmp/digests - pattern: digest-* - merge-multiple: true - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - - - name: Log in to Docker Hub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # Compute the tag for this run. Main pushes use sha- (so every - # commit gets its own immutable tag); releases use the release tag name. - - name: Compute tag - id: tag - run: | - if [ "${{ github.event_name }}" = "release" ]; then - echo "tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT" - else - echo "tag=sha-${{ github.sha }}" >> "$GITHUB_OUTPUT" - fi - - - name: Create manifest list and push - working-directory: /tmp/digests - run: | - set -euo pipefail - # Build the arg array from each digest file (filename = the digest - # hex, with no sha256: prefix; empty file content, only the name - # matters). Using an array avoids shellcheck SC2046 and keeps - # every digest a single argv token even under pathological names. - args=() - for digest_file in *; do - args+=("${IMAGE_NAME}@sha256:${digest_file}") - done - docker buildx imagetools create \ - -t "${IMAGE_NAME}:${TAG}" \ - "${args[@]}" - env: - IMAGE_NAME: ${{ env.IMAGE_NAME }} - TAG: ${{ steps.tag.outputs.tag }} - - - name: Inspect image - run: | - docker buildx imagetools inspect "${IMAGE_NAME}:${TAG}" - env: - IMAGE_NAME: ${{ env.IMAGE_NAME }} - TAG: ${{ steps.tag.outputs.tag }} - - # Signal to move-main that the SHA tag is live. Only on main pushes; - # releases set pushed_release_tag instead. - - name: Mark SHA tag pushed - id: mark_pushed - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - run: echo "pushed=true" >> "$GITHUB_OUTPUT" - - # Signal to move-latest that the release tag is live. - - name: Mark release tag pushed - id: mark_release_pushed - if: github.event_name == 'release' - run: echo "pushed=true" >> "$GITHUB_OUTPUT" - - # --------------------------------------------------------------------------- - # Move :main to point at the SHA tag the merge job pushed. - # - # :main is the floating tag that tracks the tip of the main branch. Every - # merge to main retags :main forward. Users who want "latest dev build" - # pull :main; users who want stable releases pull :latest. - # - # The real serialization guarantee comes from the top-level concurrency - # group (`docker-${{ github.ref }}` with `cancel-in-progress: false`), - # which ensures at most one workflow run for this ref executes at a time. - # That means two move-main steps for the same ref cannot overlap. - # - # This job has its own concurrency group as defense-in-depth: if the - # top-level group is ever loosened, queued move-mains will run serially - # in arrival order, each one running the ancestor check below and either - # advancing :main or skipping. `cancel-in-progress: false` matches the - # top-level setting — we don't want rapid pushes to cancel a queued - # move-main, because the ancestor check is the real safety mechanism - # and queueing is cheap (move-main is a ~30s registry op). - # - # Combined with the ancestor check, this means :main only ever moves - # forward in git history. - # --------------------------------------------------------------------------- - move-main: - if: | - github.repository == 'NousResearch/hermes-agent' - && github.event_name == 'push' - && github.ref == 'refs/heads/main' - && needs.merge.outputs.pushed_sha_tag == 'true' - needs: merge - runs-on: ubuntu-latest - timeout-minutes: 10 - concurrency: - group: docker-move-main-${{ github.ref }} - cancel-in-progress: false - steps: - - name: Checkout code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - fetch-depth: 1000 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - - - name: Log in to Docker Hub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # Read the git revision label off the current :main manifest, then - # use `git merge-base --is-ancestor` to check whether our commit is a - # descendant of it. If :main doesn't exist yet, or its label is - # missing, we treat that as "safe to publish". If another run already - # advanced :main past us (or diverged), we skip and leave it alone. - - name: Decide whether to move :main - id: main_check - run: | - set -euo pipefail - image=nousresearch/hermes-agent - - # Pull the JSON for the linux/amd64 sub-manifest's config and extract - # the OCI revision label with jq — Go template field access can't - # handle dots in map keys, so using json+jq is the robust route. - image_json=$( - docker buildx imagetools inspect "${image}:main" \ - --format '{{ json (index .Image "linux/amd64") }}' \ - 2>/dev/null || true - ) - - if [ -z "${image_json}" ]; then - echo "No existing :main (or inspect failed) — safe to publish." - echo "push_main=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - current_sha=$( - printf '%s' "${image_json}" \ - | jq -r '.config.Labels."org.opencontainers.image.revision" // ""' - ) - - if [ -z "${current_sha}" ]; then - echo "Registry :main has no revision label — safe to publish." - echo "push_main=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - echo "Registry :main is at ${current_sha}" - echo "This run is at ${GITHUB_SHA}" - - if [ "${current_sha}" = "${GITHUB_SHA}" ]; then - echo ":main already points at our SHA — nothing to do." - echo "push_main=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Make sure we have the :main commit locally for merge-base. - if ! git cat-file -e "${current_sha}^{commit}" 2>/dev/null; then - git fetch --no-tags --prune origin \ - "+refs/heads/main:refs/remotes/origin/main" \ - || true - fi - - if ! git cat-file -e "${current_sha}^{commit}" 2>/dev/null; then - echo "Registry :main points at an unknown commit (${current_sha}); refusing to overwrite." - echo "push_main=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Our SHA must be a descendant of the current :main to be safe. - if git merge-base --is-ancestor "${current_sha}" "${GITHUB_SHA}"; then - echo "Our commit is a descendant of :main — safe to advance." - echo "push_main=true" >> "$GITHUB_OUTPUT" - else - echo "Another run advanced :main past us (or diverged) — leaving it alone." - echo "push_main=false" >> "$GITHUB_OUTPUT" - fi - - # Retag the already-pushed SHA manifest as :main. This is a registry- - # side operation — no rebuild, no layer re-push — so it's quick and - # atomic per-tag. The ancestor check above plus the cancel-in-progress - # concurrency on this job together guarantee we only ever move :main - # forward in git history. - - name: Move :main to this SHA - if: steps.main_check.outputs.push_main == 'true' - run: | - set -euo pipefail - image=nousresearch/hermes-agent - docker buildx imagetools create \ - --tag "${image}:main" \ - "${image}:sha-${GITHUB_SHA}" - - # --------------------------------------------------------------------------- - # Move :latest to point at the release tag the merge job pushed. - # - # :latest is the floating tag that tracks the most recent stable release. - # Only `release: published` events advance it — never main pushes. - # - # We still run an ancestor check against the existing :latest so that a - # backport release on an older branch (e.g. patching v1.1.5 after v1.2.3 - # is out) doesn't drag :latest backwards. The check is the same shape as - # move-main: read the OCI revision label off the current :latest, look up - # that commit in git, and only advance if our release commit is a strict - # descendant. - # --------------------------------------------------------------------------- - move-latest: - if: | - github.repository == 'NousResearch/hermes-agent' - && github.event_name == 'release' - && needs.merge.outputs.pushed_release_tag == 'true' - needs: merge - runs-on: ubuntu-latest - timeout-minutes: 10 - concurrency: - group: docker-move-latest - cancel-in-progress: false - steps: - - name: Checkout code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - fetch-depth: 1000 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - - - name: Log in to Docker Hub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Decide whether to move :latest - id: latest_check - run: | - set -euo pipefail - image=nousresearch/hermes-agent - - image_json=$( - docker buildx imagetools inspect "${image}:latest" \ - --format '{{ json (index .Image "linux/amd64") }}' \ - 2>/dev/null || true - ) - - if [ -z "${image_json}" ]; then - echo "No existing :latest (or inspect failed) — safe to publish." - echo "push_latest=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - current_sha=$( - printf '%s' "${image_json}" \ - | jq -r '.config.Labels."org.opencontainers.image.revision" // ""' - ) - - if [ -z "${current_sha}" ]; then - echo "Registry :latest has no revision label — safe to publish." - echo "push_latest=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - echo "Registry :latest is at ${current_sha}" - echo "This release is at ${GITHUB_SHA}" - - if [ "${current_sha}" = "${GITHUB_SHA}" ]; then - echo ":latest already points at our SHA — nothing to do." - echo "push_latest=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Make sure we have the :latest commit locally for merge-base. - # Releases can be cut from any branch, so fetch broadly. - if ! git cat-file -e "${current_sha}^{commit}" 2>/dev/null; then - git fetch --no-tags --prune origin \ - "+refs/heads/main:refs/remotes/origin/main" \ - || true - fi - - if ! git cat-file -e "${current_sha}^{commit}" 2>/dev/null; then - echo "Registry :latest points at an unknown commit (${current_sha}); refusing to overwrite." - echo "push_latest=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Our release SHA must be a descendant of the current :latest. - # Backport releases on older branches won't satisfy this and will - # be left alone — :latest stays on the newer release. - if git merge-base --is-ancestor "${current_sha}" "${GITHUB_SHA}"; then - echo "Our release commit is a descendant of :latest — safe to advance." - echo "push_latest=true" >> "$GITHUB_OUTPUT" - else - echo "Existing :latest is newer than this release (likely a backport) — leaving it alone." - echo "push_latest=false" >> "$GITHUB_OUTPUT" - fi - - # Retag the already-pushed release manifest as :latest. - - name: Move :latest to this release tag - if: steps.latest_check.outputs.push_latest == 'true' - env: - RELEASE_TAG: ${{ needs.merge.outputs.release_tag }} - run: | - set -euo pipefail - image=nousresearch/hermes-agent - docker buildx imagetools create \ - --tag "${image}:latest" \ - "${image}:${RELEASE_TAG}" diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/docs-site-checks.yml b/reference/hermes-agent-2026.5.16/.github/workflows/docs-site-checks.yml deleted file mode 100755 index 80fe9ea..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/docs-site-checks.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Docs Site Checks - -on: - pull_request: - paths: - - 'website/**' - - '.github/workflows/docs-site-checks.yml' - workflow_dispatch: - -permissions: - contents: read - -jobs: - docs-site-checks: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 - with: - node-version: 20 - cache: npm - cache-dependency-path: website/package-lock.json - - - name: Install website dependencies - run: npm ci - working-directory: website - - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version: '3.11' - - - name: Install ascii-guard - run: python -m pip install ascii-guard==2.3.0 pyyaml==6.0.3 - - - name: Extract skill metadata for dashboard - run: python3 website/scripts/extract-skills.py - - - name: Regenerate per-skill docs pages + catalogs - run: python3 website/scripts/generate-skill-docs.py - - - name: Lint docs diagrams - run: npm run lint:diagrams - working-directory: website - - - name: Build Docusaurus - run: npm run build - working-directory: website diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/history-check.yml b/reference/hermes-agent-2026.5.16/.github/workflows/history-check.yml deleted file mode 100755 index bd66f19..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/history-check.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: History Check - -# Rejects PRs whose branch has no common ancestor with main. -# -# In May 2026 PR #25045 was merged from a branch that had been disconnected -# from main's history (likely an accidental `git checkout --orphan` or -# `.git/` re-init). GitHub's merge UI does not refuse merges of unrelated -# histories, so the PR landed cleanly with the intended one-file change — -# but its parent-less root commit (413990c94) got grafted into main as a -# second root, and ~1500 files' worth of `git blame` history collapsed -# onto that single commit. -# -# This check catches the failure mode by requiring `git merge-base` between -# the PR head and main to be non-empty. - -on: - pull_request: - branches: [main] - -permissions: - contents: read - -jobs: - check-common-ancestor: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - fetch-depth: 0 # full history both sides for merge-base - - - name: Reject PRs with no common ancestor on main - run: | - # `git merge-base` exits non-zero AND prints nothing when the two - # commits share no ancestor. We check both conditions explicitly - # so the failure message is clear regardless of which signal fires - # first. - if ! BASE=$(git merge-base origin/main HEAD 2>/dev/null) || [ -z "$BASE" ]; then - echo "" - echo "::error::This PR has no common ancestor with main." - echo "" - echo "Your branch's history is disconnected from main. Common causes:" - echo " - the branch was created with 'git checkout --orphan'" - echo " - '.git/' was re-initialized at some point during the work" - echo " - the branch was force-pushed from an unrelated repository" - echo "" - echo "Merging an unrelated-history PR grafts a parent-less root commit" - echo "into main and collapses git blame for every file in that snapshot." - echo "Reference: PR #25045 caused this and re-rooted blame on ~1500" - echo "files to a single orphan commit." - echo "" - echo "To fix, rebase your changes onto current main:" - echo " git fetch origin main" - echo " git checkout -b fix-branch origin/main" - echo " # re-apply your changes (cherry-pick, copy files, etc.)" - echo " git push -f origin fix-branch" - exit 1 - fi - echo "::notice::Common ancestor with main: $BASE" diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/lint.yml b/reference/hermes-agent-2026.5.16/.github/workflows/lint.yml deleted file mode 100755 index 807d5b6..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/lint.yml +++ /dev/null @@ -1,202 +0,0 @@ -name: Lint (ruff + ty) - -# Two things here: -# 1. Advisory diff — ruff + ty diagnostics as a diff vs the target branch. -# Posts a Markdown summary and a PR comment. Exit zero always. -# 2. Blocking ``ruff check .`` — enforces the explicit rules in -# ``[tool.ruff.lint.select]`` (currently PLW1514). Failure blocks merge. -# Separate job so the advisory diff still runs and posts even when -# enforcement fails. - -on: - push: - branches: [main] - paths-ignore: - - "**/*.md" - - "docs/**" - - "website/**" - pull_request: - branches: [main] - paths-ignore: - - "**/*.md" - - "docs/**" - - "website/**" - -permissions: - contents: read - pull-requests: write # needed to post/update PR comments - -concurrency: - group: lint-${{ github.ref }} - cancel-in-progress: true - -jobs: - lint-diff: - name: ruff + ty diff - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - name: Checkout code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - fetch-depth: 0 # need full history for merge-base + worktree - - - name: Install uv - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 - - - name: Install ruff + ty - run: | - uv tool install ruff - uv tool install ty - - - name: Determine base ref - id: base - run: | - # For PRs, diff against the merge base with the target branch. - # For pushes to main, diff against the previous commit on main. - if [ "${{ github.event_name }}" = "pull_request" ]; then - BASE_SHA=$(git merge-base "origin/${{ github.base_ref }}" HEAD) - BASE_REF="origin/${{ github.base_ref }}" - else - BASE_SHA=$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD) - BASE_REF="HEAD~1" - fi - echo "sha=${BASE_SHA}" >> "$GITHUB_OUTPUT" - echo "ref=${BASE_REF}" >> "$GITHUB_OUTPUT" - echo "Base SHA: ${BASE_SHA}" - echo "Base ref: ${BASE_REF}" - - - name: Run ruff + ty on HEAD - run: | - mkdir -p .lint-reports/head - ruff check --output-format json --exit-zero \ - > .lint-reports/head/ruff.json || true - ty check --output-format gitlab --exit-zero \ - > .lint-reports/head/ty.json || true - echo "HEAD ruff: $(wc -c < .lint-reports/head/ruff.json) bytes" - echo "HEAD ty: $(wc -c < .lint-reports/head/ty.json) bytes" - - - name: Run ruff + ty on base (via git worktree) - run: | - mkdir -p .lint-reports/base - # Use a worktree so we don't clobber the main checkout. If the basex - # SHA is identical to HEAD (e.g. first commit), skip and leave the - # base reports empty — the diff script handles missing files. - HEAD_SHA=$(git rev-parse HEAD) - BASE_SHA="${{ steps.base.outputs.sha }}" - if [ "$BASE_SHA" = "$HEAD_SHA" ]; then - echo "Base SHA == HEAD SHA, skipping base scan." - echo '[]' > .lint-reports/base/ruff.json - echo '[]' > .lint-reports/base/ty.json - else - git worktree add --detach /tmp/lint-base "$BASE_SHA" - ( - cd /tmp/lint-base - ruff check --output-format json --exit-zero \ - > "$GITHUB_WORKSPACE/.lint-reports/base/ruff.json" || true - ty check --output-format gitlab --exit-zero \ - > "$GITHUB_WORKSPACE/.lint-reports/base/ty.json" || true - ) - git worktree remove --force /tmp/lint-base - fi - echo "base ruff: $(wc -c < .lint-reports/base/ruff.json) bytes" - echo "base ty: $(wc -c < .lint-reports/base/ty.json) bytes" - - - name: Generate diff summary - run: | - python scripts/lint_diff.py \ - --base-ruff .lint-reports/base/ruff.json \ - --head-ruff .lint-reports/head/ruff.json \ - --base-ty .lint-reports/base/ty.json \ - --head-ty .lint-reports/head/ty.json \ - --base-ref "${{ steps.base.outputs.ref }}" \ - --head-ref "${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }}" \ - --output .lint-reports/summary.md - cat .lint-reports/summary.md >> "$GITHUB_STEP_SUMMARY" - - - name: Upload reports as artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: lint-reports - path: .lint-reports/ - retention-days: 14 - - - name: Post / update PR comment - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository - continue-on-error: true - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 - with: - script: | - const fs = require('fs'); - const body = fs.readFileSync('.lint-reports/summary.md', 'utf8'); - const marker = ''; - const fullBody = marker + '\n' + body; - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - const existing = comments.find(c => c.body && c.body.includes(marker)); - if (existing) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: fullBody, - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: fullBody, - }); - } - - - ruff-blocking: - # Enforce the rules in pyproject.toml [tool.ruff.lint.select]. Currently - # PLW1514 (unspecified-encoding) — catches bare ``open()`` / - # ``read_text()`` / ``write_text()`` calls that default to locale - # encoding on Windows. Failure here blocks merge; the advisory - # ``lint-diff`` job above runs independently so reviewers still get - # the diff comment even when enforcement fails. - name: ruff enforcement (blocking) - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Checkout code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - - name: Install uv - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 - - - name: Install ruff - run: uv tool install ruff - - - name: ruff check . - # No --exit-zero, no || true. Exit code propagates to the job, - # which propagates to the required-check gate. - run: | - ruff check . - - windows-footguns: - # Static guardrails on Windows-unsafe Python primitives — os.kill(pid, 0), - # os.killpg, os.setsid, signal.SIGKILL without getattr fallback, - # shebang scripts via subprocess, bare open() without encoding=, etc. - # See scripts/check-windows-footguns.py for the full rule list. - name: Windows footguns (blocking) - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Checkout code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - - name: Set up Python - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5 - with: - python-version: "3.11" - - - name: Run footgun checker - run: python scripts/check-windows-footguns.py --all diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/nix-lockfile-fix.yml b/reference/hermes-agent-2026.5.16/.github/workflows/nix-lockfile-fix.yml deleted file mode 100755 index b5e02c3..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/nix-lockfile-fix.yml +++ /dev/null @@ -1,254 +0,0 @@ -name: Nix Lockfile Fix - -on: - push: - branches: [main] - paths: - - 'ui-tui/package-lock.json' - - 'ui-tui/package.json' - - 'web/package-lock.json' - - 'web/package.json' - workflow_dispatch: - inputs: - pr_number: - description: 'PR number to fix (leave empty to run on the selected branch)' - required: false - type: string - issue_comment: - types: [edited] - -permissions: - contents: write - pull-requests: write - -concurrency: - group: nix-lockfile-fix-${{ github.event.issue.number || github.event.inputs.pr_number || github.ref }} - cancel-in-progress: false - -jobs: - # ── Auto-fix on main ─────────────────────────────────────────────── - # Fires when a push to main touches package.json or package-lock.json - # in ui-tui/ or web/. Runs fix-lockfiles and pushes the hash - # update commit directly to main so Nix builds never stay broken. - # - # Safety invariants: - # 1. The fix commit only touches nix/*.nix files, which are NOT in - # the paths filter above, so this cannot re-trigger itself. - # 2. An explicit file-whitelist check before commit aborts if - # fix-lockfiles ever modifies unexpected files. - # 3. Job-level concurrency with cancel-in-progress: true ensures - # back-to-back pushes collapse to the newest; ref: main checkout - # always operates on the latest branch state. - # 4. Uses a GitHub App token (not GITHUB_TOKEN) so the fix commit - # triggers downstream nix.yml verification. - auto-fix-main: - if: github.event_name == 'push' - runs-on: ubuntu-latest - timeout-minutes: 25 - concurrency: - group: auto-fix-main - cancel-in-progress: true - steps: - - name: Generate GitHub App token - id: app-token - uses: actions/create-github-app-token@7bfa3a4717ef143a604ee0a99d859b8886a96d00 # v1.9.3 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - ref: main - token: ${{ steps.app-token.outputs.token }} - - - uses: ./.github/actions/nix-setup - with: - cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - - - name: Apply lockfile hashes - id: apply - run: nix run .#fix-lockfiles -- --apply - - - name: Commit & push - if: steps.apply.outputs.changed == 'true' - shell: bash - run: | - set -euo pipefail - - # Ensure only nix files were modified — prevents accidental - # self-triggering if fix-lockfiles ever touches package files. - unexpected="$(git diff --name-only | grep -Ev '^nix/(tui|web)\.nix$' || true)" - if [ -n "$unexpected" ]; then - echo "::error::Unexpected modified files: $unexpected" - exit 1 - fi - - # Record the base SHA before committing — used to detect package - # file changes if we need to rebase after a non-fast-forward push. - BASE_SHA="$(git rev-parse HEAD)" - - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add nix/tui.nix nix/web.nix - git commit -m "fix(nix): auto-refresh npm lockfile hashes" \ - -m "Source: $GITHUB_SHA" \ - -m "Run: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" - - # Retry push with rebase in case main advanced with an unrelated - # commit during the nix build. Without this, a non-fast-forward - # rejection silently loses the fix. If package files changed during - # the rebase, abort — a fresh auto-fix run will handle the new state. - for attempt in 1 2 3; do - if git push origin HEAD:main; then - exit 0 - fi - echo "::warning::Push attempt $attempt failed (non-fast-forward?), rebasing…" - git fetch origin main - - # If package files changed between our base and the new main, - # our computed hashes are stale. Abort and let the next triggered - # run recompute from the correct package-lock state. - pkg_changed="$(git diff --name-only "$BASE_SHA"..origin/main -- \ - 'ui-tui/package-lock.json' 'ui-tui/package.json' \ - 'web/package-lock.json' 'web/package.json' || true)" - if [ -n "$pkg_changed" ]; then - echo "::warning::Package files changed since hash computation — aborting; a fresh run will recompute" - exit 0 - fi - - git rebase origin/main - done - echo "::error::Failed to push after 3 rebase attempts" - exit 1 - - # ── PR fix (manual / checkbox) ───────────────────────────────────── - # Existing behavior: run on manual dispatch OR when a task-list - # checkbox in the sticky lockfile-check comment flips from [ ] to [x]. - fix: - if: | - github.event_name == 'workflow_dispatch' || - (github.event_name == 'issue_comment' - && github.event.issue.pull_request != null - && contains(github.event.comment.body, '[x] **Apply lockfile fix**') - && !contains(github.event.changes.body.from, '[x] **Apply lockfile fix**')) - runs-on: ubuntu-latest - timeout-minutes: 25 - steps: - - name: Authorize & resolve PR - id: resolve - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - with: - script: | - // 1. Verify the actor has write access — applies to both checkbox - // clicks and manual dispatch. - const { data: perm } = - await github.rest.repos.getCollaboratorPermissionLevel({ - owner: context.repo.owner, - repo: context.repo.repo, - username: context.actor, - }); - if (!['admin', 'write', 'maintain'].includes(perm.permission)) { - core.setFailed( - `${context.actor} lacks write access (has: ${perm.permission})` - ); - return; - } - - // 2. Resolve which ref to check out. - let prNumber = ''; - if (context.eventName === 'issue_comment') { - prNumber = String(context.payload.issue.number); - } else if (context.eventName === 'workflow_dispatch') { - prNumber = context.payload.inputs.pr_number || ''; - } - - if (!prNumber) { - core.setOutput('ref', context.ref.replace(/^refs\/heads\//, '')); - core.setOutput('repo', context.repo.repo); - core.setOutput('owner', context.repo.owner); - core.setOutput('pr', ''); - return; - } - - const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: Number(prNumber), - }); - core.setOutput('ref', pr.head.ref); - core.setOutput('repo', pr.head.repo.name); - core.setOutput('owner', pr.head.repo.owner.login); - core.setOutput('pr', String(pr.number)); - - # Wipe the sticky lockfile-check comment to a "running" state as soon - # as the job is authorized, so the user sees their click was picked up - # before the ~minute of nix build work. - - name: Mark sticky as running - if: steps.resolve.outputs.pr != '' - uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 - with: - header: nix-lockfile-check - number: ${{ steps.resolve.outputs.pr }} - message: | - ### 🔄 Applying lockfile fix… - - Triggered by @${{ github.actor }} — [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). - - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - repository: ${{ steps.resolve.outputs.owner }}/${{ steps.resolve.outputs.repo }} - ref: ${{ steps.resolve.outputs.ref }} - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 - - - uses: ./.github/actions/nix-setup - with: - cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - - - name: Apply lockfile hashes - id: apply - run: nix run .#fix-lockfiles - - - name: Commit & push - if: steps.apply.outputs.changed == 'true' - shell: bash - run: | - set -euo pipefail - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add nix/tui.nix nix/web.nix - git commit -m "fix(nix): refresh npm lockfile hashes" - git push - - - name: Update sticky (applied) - if: steps.apply.outputs.changed == 'true' && steps.resolve.outputs.pr != '' - uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 - with: - header: nix-lockfile-check - number: ${{ steps.resolve.outputs.pr }} - message: | - ### ✅ Lockfile fix applied - - Pushed a commit refreshing the npm lockfile hashes — [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). - - - name: Update sticky (already current) - if: steps.apply.outputs.changed == 'false' && steps.resolve.outputs.pr != '' - uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 - with: - header: nix-lockfile-check - number: ${{ steps.resolve.outputs.pr }} - message: | - ### ✅ Lockfile hashes already current - - Nothing to commit — [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). - - - name: Update sticky (failed) - if: failure() && steps.resolve.outputs.pr != '' - uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 - with: - header: nix-lockfile-check - number: ${{ steps.resolve.outputs.pr }} - message: | - ### ❌ Lockfile fix failed - - See the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for logs. diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/nix.yml b/reference/hermes-agent-2026.5.16/.github/workflows/nix.yml deleted file mode 100755 index 9a8f45a..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/nix.yml +++ /dev/null @@ -1,117 +0,0 @@ -name: Nix - -on: - push: - branches: [main] - pull_request: - -permissions: - contents: read - pull-requests: write - -concurrency: - group: nix-${{ github.ref }} - cancel-in-progress: true - -jobs: - nix: - strategy: - matrix: - os: [ubuntu-latest, macos-latest] - runs-on: ${{ matrix.os }} - timeout-minutes: 30 - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: ./.github/actions/nix-setup - with: - cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} - - - name: Resolve head SHA - if: github.event_name == 'pull_request' - id: sha - shell: bash - run: | - FULL="${{ github.event.pull_request.head.sha || github.sha }}" - echo "full=$FULL" >> "$GITHUB_OUTPUT" - echo "short=${FULL:0:7}" >> "$GITHUB_OUTPUT" - - - name: Check flake - id: flake - if: runner.os == 'Linux' - continue-on-error: true - run: nix flake check --print-build-logs - - - name: Build package - id: build - if: runner.os == 'Linux' - continue-on-error: true - run: nix build --print-build-logs - - # When the real Nix build fails, run a targeted diagnostic to see if - # the failure is specifically a stale npm lockfile hash in one of the - # known npm subpackages (tui / web). This avoids surfacing a generic - # "build failed" message when the fix is a single known command. - - name: Diagnose npm lockfile hashes - id: hash_check - if: (steps.flake.outcome == 'failure' || steps.build.outcome == 'failure') && runner.os == 'Linux' - continue-on-error: true - env: - LINK_SHA: ${{ steps.sha.outputs.full }} - run: nix run .#fix-lockfiles -- --check - - # If fix-lockfiles itself crashes (infrastructure blip, cache throttle, - # etc.) it won't set stale=true/false. Treat that as a distinct failure - # mode rather than silently ignoring it. - - name: Fail if hash check crashed without reporting - if: steps.hash_check.outcome == 'failure' && steps.hash_check.outputs.stale != 'true' && steps.hash_check.outputs.stale != 'false' - run: | - echo "::error::fix-lockfiles exited without reporting stale status — likely an infrastructure or script failure" - exit 1 - - - name: Post sticky PR comment (stale hashes) - if: steps.hash_check.outputs.stale == 'true' && github.event_name == 'pull_request' - uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 - with: - header: nix-lockfile-check - message: | - ### ⚠️ npm lockfile hash out of date - - Checked against commit [`${{ steps.sha.outputs.short }}`](${{ github.server_url }}/${{ github.repository }}/commit/${{ steps.sha.outputs.full }}) (PR head at check time). - - The `hash = "sha256-..."` line in these nix files no longer matches the committed `package-lock.json`: - - ${{ steps.hash_check.outputs.report }} - - #### Apply the fix - - - [ ] **Apply lockfile fix** — tick to push a commit with the correct hashes to this PR branch - - Or [run the Nix Lockfile Fix workflow](${{ github.server_url }}/${{ github.repository }}/actions/workflows/nix-lockfile-fix.yml) manually (pass PR `#${{ github.event.pull_request.number }}`) - - Or locally: `nix run .#fix-lockfiles` and commit the diff - - # Clear the sticky comment when either the build passed outright (no - # hash check needed) or the hash check explicitly returned stale=false - # (build failed for a non-hash reason). - - name: Clear sticky PR comment (resolved) - if: | - github.event_name == 'pull_request' && - runner.os == 'Linux' && - (steps.hash_check.outputs.stale == 'false' || - (steps.flake.outcome == 'success' && steps.build.outcome == 'success')) - uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 - with: - header: nix-lockfile-check - delete: true - - - name: Final fail if build or flake failed - if: steps.flake.outcome == 'failure' || steps.build.outcome == 'failure' - run: | - if [ "${{ steps.hash_check.outputs.stale }}" == "true" ]; then - echo "::error::Nix build failed due to stale npm lockfile hash. Run: nix run .#fix-lockfiles" - else - echo "::error::Nix build/flake check failed. See logs above." - fi - exit 1 - - - name: Evaluate flake (macOS) - if: runner.os == 'macOS' - run: nix flake show --json > /dev/null diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/osv-scanner.yml b/reference/hermes-agent-2026.5.16/.github/workflows/osv-scanner.yml deleted file mode 100755 index db8c3d7..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/osv-scanner.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: OSV-Scanner - -# Scans lockfiles (uv.lock, package-lock.json) against the OSV vulnerability -# database. Runs on every PR that touches a lockfile and on a weekly schedule -# against main. -# -# This is detection-only — OSV-Scanner does NOT open PRs or modify pins. -# It reports known CVEs in currently-pinned dependency versions so we can -# decide when and how to patch on our own schedule. Our pinning strategy -# (full SHA / exact version) is preserved; only the notification signal -# is added. -# -# Complements the existing supply-chain-audit.yml workflow (which scans -# for malicious code patterns in PR diffs) by covering the orthogonal -# "currently-pinned dep became known-vulnerable" case. -# -# Uses Google's officially-recommended reusable workflow, pinned by SHA. -# Findings land in the repo's Security tab (Code Scanning > OSV-Scanner). -# fail-on-vuln is disabled so the job does not block merges on pre-existing -# vulnerabilities in pinned deps that we may need to patch deliberately. - -on: - pull_request: - branches: [main] - paths: - - 'uv.lock' - - 'pyproject.toml' - - 'package.json' - - 'package-lock.json' - - 'ui-tui/package.json' - - 'ui-tui/package-lock.json' - - 'website/package.json' - - 'website/package-lock.json' - - '.github/workflows/osv-scanner.yml' - push: - branches: [main] - paths: - - 'uv.lock' - - 'pyproject.toml' - - 'package.json' - - 'package-lock.json' - - 'ui-tui/package-lock.json' - - 'website/package-lock.json' - schedule: - # Weekly scan against main — catches CVEs published after merge for - # deps that haven't changed since. - - cron: '0 9 * * 1' - workflow_dispatch: - -permissions: - # Required by the reusable workflow to upload SARIF to the Security tab. - actions: read - contents: read - security-events: write - -jobs: - scan: - name: Scan lockfiles - uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@c51854704019a247608d928f370c98740469d4b5 # v2.3.5 - with: - # Scan explicit lockfiles rather than recursing, so we only look at - # the three sources of truth and skip vendored / test / worktree dirs. - scan-args: |- - --lockfile=uv.lock - --lockfile=ui-tui/package-lock.json - --lockfile=website/package-lock.json - fail-on-vuln: false diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/skills-index.yml b/reference/hermes-agent-2026.5.16/.github/workflows/skills-index.yml deleted file mode 100755 index 8beda19..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/skills-index.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: Build Skills Index - -on: - schedule: - # Run twice daily: 6 AM and 6 PM UTC - - cron: '0 6,18 * * *' - workflow_dispatch: # Manual trigger - push: - branches: [main] - paths: - - 'scripts/build_skills_index.py' - - '.github/workflows/skills-index.yml' - -permissions: - contents: read - -jobs: - build-index: - # Only run on the upstream repository, not on forks - if: github.repository == 'NousResearch/hermes-agent' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: pip install httpx==0.28.1 pyyaml==6.0.2 - - - name: Build skills index - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: python scripts/build_skills_index.py - - - name: Upload index artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: skills-index - path: website/static/api/skills-index.json - retention-days: 7 - - deploy-with-index: - needs: build-index - runs-on: ubuntu-latest - permissions: - pages: write - id-token: write - environment: - name: github-pages - url: ${{ steps.deploy.outputs.page_url }} - # Only deploy on schedule or manual trigger (not on every push to the script) - if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - name: skills-index - path: website/static/api/ - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 - with: - node-version: 20 - cache: npm - cache-dependency-path: website/package-lock.json - - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version: '3.11' - - - name: Install PyYAML for skill extraction - run: pip install pyyaml==6.0.2 - - - name: Extract skill metadata for dashboard - run: python3 website/scripts/extract-skills.py - - - name: Install dependencies - run: npm ci - working-directory: website - - - name: Build Docusaurus - run: npm run build - working-directory: website - - - name: Stage deployment - run: | - mkdir -p _site/docs - cp -r landingpage/* _site/ - cp -r website/build/* _site/docs/ - echo "hermes-agent.nousresearch.com" > _site/CNAME - - - name: Upload artifact - uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 - with: - path: _site - - - name: Deploy to GitHub Pages - id: deploy - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/supply-chain-audit.yml b/reference/hermes-agent-2026.5.16/.github/workflows/supply-chain-audit.yml deleted file mode 100755 index 69a9a11..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/supply-chain-audit.yml +++ /dev/null @@ -1,205 +0,0 @@ -name: Supply Chain Audit - -on: - pull_request: - types: [opened, synchronize, reopened] - paths: - - '**/*.py' - - '**/*.pth' - - '**/setup.py' - - '**/setup.cfg' - - '**/sitecustomize.py' - - '**/usercustomize.py' - - '**/__init__.pth' - - 'pyproject.toml' - -permissions: - pull-requests: write - contents: read - -# Narrow, high-signal scanner. Only fires on critical indicators of supply -# chain attacks (e.g. the litellm-style payloads). Low-signal heuristics -# (plain base64, plain exec/eval, dependency/Dockerfile/workflow edits, -# Actions version unpinning, outbound POST/PUT) were intentionally -# removed — they fired on nearly every PR and trained reviewers to ignore -# the scanner. Keep this file's checks ruthlessly narrow: if you find -# yourself adding WARNING-tier patterns here again, make a separate -# advisory-only workflow instead. - -jobs: - scan: - name: Scan PR for critical supply chain risks - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - fetch-depth: 0 - - - name: Scan diff for critical patterns - id: scan - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -euo pipefail - - BASE="${{ github.event.pull_request.base.sha }}" - HEAD="${{ github.event.pull_request.head.sha }}" - - # Added lines only, excluding lockfiles. - DIFF=$(git diff "$BASE".."$HEAD" -- . ':!uv.lock' ':!*.lock' ':!package-lock.json' ':!yarn.lock' || true) - - FINDINGS="" - - # --- .pth files (auto-execute on Python startup) --- - # The exact mechanism used in the litellm supply chain attack: - # https://github.com/BerriAI/litellm/issues/24512 - PTH_FILES=$(git diff --name-only "$BASE".."$HEAD" | grep '\.pth$' || true) - if [ -n "$PTH_FILES" ]; then - FINDINGS="${FINDINGS} - ### 🚨 CRITICAL: .pth file added or modified - Python \`.pth\` files in \`site-packages/\` execute automatically when the interpreter starts — no import required. - - **Files:** - \`\`\` - ${PTH_FILES} - \`\`\` - " - fi - - # --- base64 decode + exec/eval on the same line (the litellm attack pattern) --- - B64_EXEC_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -iE 'base64\.(b64decode|decodebytes|urlsafe_b64decode)' | grep -iE 'exec\(|eval\(' | head -10 || true) - if [ -n "$B64_EXEC_HITS" ]; then - FINDINGS="${FINDINGS} - ### 🚨 CRITICAL: base64 decode + exec/eval combo - Base64-decoded strings passed directly to exec/eval — the signature of hidden credential-stealing payloads. - - **Matches:** - \`\`\` - ${B64_EXEC_HITS} - \`\`\` - " - fi - - # --- subprocess with encoded/obfuscated command argument --- - PROC_HITS=$(echo "$DIFF" | grep -n '^\+' | grep -E 'subprocess\.(Popen|call|run)\s*\(' | grep -iE 'base64|\\x[0-9a-f]{2}|chr\(' | head -10 || true) - if [ -n "$PROC_HITS" ]; then - FINDINGS="${FINDINGS} - ### 🚨 CRITICAL: subprocess with encoded/obfuscated command - Subprocess calls whose command strings are base64- or hex-encoded are a strong indicator of payload execution. - - **Matches:** - \`\`\` - ${PROC_HITS} - \`\`\` - " - fi - - # --- Install-hook files (setup.py/sitecustomize/usercustomize/__init__.pth) --- - # These execute during pip install or interpreter startup. - SETUP_HITS=$(git diff --name-only "$BASE".."$HEAD" | grep -E '(^|/)(setup\.py|setup\.cfg|sitecustomize\.py|usercustomize\.py|__init__\.pth)$' || true) - if [ -n "$SETUP_HITS" ]; then - FINDINGS="${FINDINGS} - ### 🚨 CRITICAL: Install-hook file added or modified - These files can execute code during package installation or interpreter startup. - - **Files:** - \`\`\` - ${SETUP_HITS} - \`\`\` - " - fi - - if [ -n "$FINDINGS" ]; then - echo "found=true" >> "$GITHUB_OUTPUT" - echo "$FINDINGS" > /tmp/findings.md - else - echo "found=false" >> "$GITHUB_OUTPUT" - fi - - - name: Post critical finding comment - if: steps.scan.outputs.found == 'true' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - BODY="## 🚨 CRITICAL Supply Chain Risk Detected - - This PR contains a pattern that has been used in real supply chain attacks. A maintainer must review the flagged code carefully before merging. - - $(cat /tmp/findings.md) - - --- - *Scanner only fires on high-signal indicators: .pth files, base64+exec/eval combos, subprocess with encoded commands, or install-hook files. Low-signal warnings were removed intentionally — if you're seeing this comment, the finding is worth inspecting.*" - - gh pr comment "${{ github.event.pull_request.number }}" --body "$BODY" || echo "::warning::Could not post PR comment (expected for fork PRs — GITHUB_TOKEN is read-only)" - - - name: Fail on critical findings - if: steps.scan.outputs.found == 'true' - run: | - echo "::error::CRITICAL supply chain risk patterns detected in this PR. See the PR comment for details." - exit 1 - - dep-bounds: - name: Check PyPI dependency upper bounds - runs-on: ubuntu-latest - if: contains(github.event.pull_request.changed_files_url, 'pyproject.toml') || true - steps: - - name: Checkout - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - with: - fetch-depth: 0 - - - name: Check for unbounded PyPI deps - id: bounds - run: | - set -euo pipefail - - BASE="${{ github.event.pull_request.base.sha }}" - HEAD="${{ github.event.pull_request.head.sha }}" - - # Only check added lines in pyproject.toml - ADDED=$(git diff "$BASE".."$HEAD" -- pyproject.toml | grep '^+' | grep -v '^+++' || true) - - if [ -z "$ADDED" ]; then - echo "found=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Match PyPI dep specs that have >= but no < ceiling. - # Pattern: "package>=version" without a following ",<" bound. - # Excludes git+ URLs (which use commit SHAs) and comments. - UNBOUNDED=$(echo "$ADDED" | grep -oE '"[a-zA-Z0-9_-]+(\[[^\]]*\])?>=[ 0-9.]+"' | grep -v ',<' || true) - - if [ -n "$UNBOUNDED" ]; then - echo "found=true" >> "$GITHUB_OUTPUT" - echo "$UNBOUNDED" > /tmp/unbounded.txt - else - echo "found=false" >> "$GITHUB_OUTPUT" - fi - - - name: Post unbounded dep warning - if: steps.bounds.outputs.found == 'true' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - BODY="## ⚠️ Unbounded PyPI Dependency Detected - - This PR adds PyPI dependencies without a \`=floor,=1.2.0,<2\"\` - - --- - *See PR #2810 and CONTRIBUTING.md for the full policy rationale.*" - - gh pr comment "${{ github.event.pull_request.number }}" --body "$BODY" || echo "::warning::Could not post PR comment (expected for fork PRs)" - - - name: Fail on unbounded deps - if: steps.bounds.outputs.found == 'true' - run: | - echo "::error::PyPI dependencies without upper bounds detected. Add /dev/null 2>&1; then - echo "Release $GITHUB_REF_NAME found" - exit 0 - fi - echo "Waiting for release... ($i/30)" - sleep 10 - done - echo "::warning::Release $GITHUB_REF_NAME not found after 5 minutes — skipping signature upload" - echo "skip_sign=true" >> "$GITHUB_ENV" - - - name: Sign with Sigstore - if: env.skip_sign != 'true' - uses: sigstore/gh-action-sigstore-python@f514d46b907ebcd5bedc05145c03b69c1edd8b46 # v3.0.0 - with: - inputs: >- - ./dist/*.tar.gz - ./dist/*.whl - - - name: Attach signed artifacts to GitHub Release - if: env.skip_sign != 'true' - env: - GITHUB_TOKEN: ${{ github.token }} - # release.py already created the GitHub Release — just upload - # the Sigstore signatures alongside the existing assets. - run: >- - gh release upload - "$GITHUB_REF_NAME" dist/*.sigstore.json - --repo "$GITHUB_REPOSITORY" - --clobber diff --git a/reference/hermes-agent-2026.5.16/.github/workflows/uv-lockfile-check.yml b/reference/hermes-agent-2026.5.16/.github/workflows/uv-lockfile-check.yml deleted file mode 100755 index 190a162..0000000 --- a/reference/hermes-agent-2026.5.16/.github/workflows/uv-lockfile-check.yml +++ /dev/null @@ -1,119 +0,0 @@ -name: uv.lock check - -# Verify uv.lock is in sync with pyproject.toml. Blocking check — PRs -# that modify pyproject.toml without regenerating uv.lock (or vice versa) -# must not merge, because the Docker build's `uv sync --frozen` step will -# fail on a stale lockfile and we'd rather catch it here than in the -# docker-publish workflow on main. -# -# ───────────────────────────────────────────────────────────────────────── -# IMPORTANT: this check runs against the MERGED state, not just your branch -# ───────────────────────────────────────────────────────────────────────── -# -# For `pull_request` events, GitHub checks out `refs/pull//merge` by -# default — a synthetic commit that merges your PR branch into the CURRENT -# state of `main`. That means the pyproject.toml evaluated here is -# `main's pyproject.toml + your PR's changes to pyproject.toml`, not just -# what's on your branch. -# -# Failure mode this creates: if `main` has advanced since you branched -# (e.g. someone merged a PR that added a dep to pyproject.toml + its -# corresponding uv.lock entries), your branch's uv.lock is missing those -# new entries. `uv lock --check` resolves against the merged pyproject -# and sees a lockfile that doesn't cover all the current deps → fails -# with "The lockfile at uv.lock needs to be updated." -# -# This can be confusing: `uv lock --check` passes locally (your branch -# is internally consistent) but fails in CI (merged state isn't). -# -# Fix is to sync your branch with main and regenerate the lockfile: -# -# git fetch origin main -# git rebase origin/main # or merge, whatever the repo prefers -# uv lock # regenerates uv.lock against new pyproject.toml -# git add uv.lock -# git commit -m "chore: refresh uv.lock after rebase onto main" -# git push --force-with-lease # if you rebased -# -# If you also changed pyproject.toml in your PR, `uv lock` handles that -# at the same time — one regeneration covers both your changes and the -# drift from main. -# -# This is the correct behavior! The check is protecting main's Docker -# build: a post-merge build would see the same merged state and fail -# the same way. Better to catch it here than after merge. - -on: - push: - branches: [main] - paths: - - 'pyproject.toml' - - 'uv.lock' - - '.github/workflows/uv-lockfile-check.yml' - pull_request: - branches: [main] - paths: - - 'pyproject.toml' - - 'uv.lock' - - '.github/workflows/uv-lockfile-check.yml' - -permissions: - contents: read - -concurrency: - group: uv-lockfile-check-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -jobs: - check: - name: uv lock --check - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Checkout code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - - name: Install uv - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 - - # `uv lock --check` re-resolves the project from pyproject.toml and - # compares the result to uv.lock, exiting non-zero if they disagree. - # No network writes, no file modifications. - # - # On PRs this runs against the merge commit (see comment at the top - # of this file) — failures often mean "your branch is behind main, - # rebase and regenerate uv.lock." - - name: Verify uv.lock is up-to-date - run: | - if ! uv lock --check; then - cat <<'EOF' >> "$GITHUB_STEP_SUMMARY" - ## ❌ uv.lock is out of sync with pyproject.toml - - **If this is a PR:** this check runs against the merged state - (your branch + current `main`), not just your branch. If - `uv lock --check` passes locally, your branch is likely behind - `main` — recent changes to `pyproject.toml` on `main` aren't - reflected in your branch's `uv.lock` yet. - - To fix, sync with main and regenerate the lockfile: - - ```bash - git fetch origin main - git rebase origin/main # or `git merge origin/main` - uv lock # regenerate against new pyproject.toml - git add uv.lock - git commit -m "chore: refresh uv.lock after syncing with main" - git push --force-with-lease # drop --force-with-lease if you merged - ``` - - **If you only changed pyproject.toml:** run `uv lock` locally - and commit the result. - - This check is blocking because the Docker image build uses - `uv sync --frozen --extra all`, which rejects stale lockfiles - — catching it here avoids a ~15 min failed docker-publish run - on `main` post-merge. - EOF - echo "::error title=uv.lock out of sync::Run \`uv lock\` locally and commit the result. If on a PR, sync with main first." - exit 1 - fi diff --git a/reference/hermes-agent-2026.5.16/.gitignore b/reference/hermes-agent-2026.5.16/.gitignore deleted file mode 100755 index 37b1f60..0000000 --- a/reference/hermes-agent-2026.5.16/.gitignore +++ /dev/null @@ -1,75 +0,0 @@ -.DS_Store -/venv/ -/_pycache/ -*.pyc* -__pycache__/ -.venv/ -.vscode/ -.env -.env.local -.env.development.local -.env.test.local -.env.production.local -.env.development -.env.test -export* -__pycache__/model_tools.cpython-310.pyc -__pycache__/web_tools.cpython-310.pyc -logs/ -data/ -.pytest_cache/ -tmp/ -temp_vision_images/ -hermes-*/* -examples/ -tests/quick_test_dataset.jsonl -tests/sample_dataset.jsonl -run_datagen_kimik2-thinking.sh -run_datagen_megascience_glm4-6.sh -run_datagen_sonnet.sh -source-data/* -run_datagen_megascience_glm4-6.sh -data/* -node_modules/ -browser-use/ -agent-browser/ -# Private keys -*.ppk -*.pem -privvy* -images/ -__pycache__/ -hermes_agent.egg-info/ -wandb/ -testlogs - -# CLI config (may contain sensitive SSH paths) -cli-config.yaml - -# Skills Hub state (lives in ~/.hermes/skills/.hub/ at runtime, but just in case) -skills/.hub/ -ignored/ -.worktrees/ -environments/benchmarks/evals/ - -# Web UI build output -hermes_cli/web_dist/ - -# Web UI assets — synced from @nous-research/ui at build time via -# `npm run sync-assets` (see web/package.json). -web/public/fonts/ -web/public/ds-assets/ - -# Release script temp files -.release_notes.md -mini-swe-agent/ - -# Nix -.direnv/ -.nix-stamps/ -result -website/static/api/skills-index.json -models-dev-upstream/ -hermes_cli/tui_dist/* -hermes_cli/scripts/ -docs/superpowers/* \ No newline at end of file diff --git a/reference/hermes-agent-2026.5.16/.gitkeep b/reference/hermes-agent-2026.5.16/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/reference/hermes-agent-2026.5.16/.mailmap b/reference/hermes-agent-2026.5.16/.mailmap deleted file mode 100755 index 3f093fb..0000000 --- a/reference/hermes-agent-2026.5.16/.mailmap +++ /dev/null @@ -1,108 +0,0 @@ -# .mailmap — canonical author mapping for git shortlog / git log / GitHub -# Format: Canonical Name -# See: https://git-scm.com/docs/gitmailmap -# -# This maps commit emails to GitHub noreply addresses so that: -# 1. `git shortlog -sn` shows deduplicated contributor counts -# 2. GitHub's contributor graph can attribute commits correctly -# 3. Contributors with personal/work emails get proper credit -# -# When adding entries: use the contributor's GitHub noreply email as canonical -# so GitHub can link commits to their profile. - -# === Teknium (multiple emails) === -Teknium <127238744+teknium1@users.noreply.github.com> -Teknium <127238744+teknium1@users.noreply.github.com> - -# === Contributors — personal/work emails mapped to GitHub noreply === -# Format: Canonical Name - -# Verified via GH API email search -luyao618 <364939526@qq.com> <364939526@qq.com> -ethernet8023 -nicoloboschi -cherifya -BongSuCHOI -dsocolobsky -pefontana -Helmi -hata1234 - -# Verified via PR investigation / salvage PR bodies -DeployFaith -flobo3 -gaixianggeng -KUSH42 -konsisumer -WorldInnovationsDepartment -m0n5t3r -sprmn24 -fancydirty -fxfitz -limars874 -AaronWong1999 -dippwho -duerzy -geoffwellman -hcshen0111 -jamesarch -stephenschoettler -Tranquil-Flow -Dusk1e -Awsh1 -WAXLYY -donrhmexe -hqhq1025 <1506751656@qq.com> <1506751656@qq.com> -BlackishGreen33 -tomqiaozc -MagicRay1217 -aaronagent <1115117931@qq.com> <1115117931@qq.com> -YoungYang963 -LongOddCode -Cafexss -Cygra -DomGrieco - -# Duplicate email mapping (same person, multiple emails) -Sertug17 <104278804+Sertug17@users.noreply.github.com> -yyovil -DomGrieco -dsocolobsky -olafthiele - -# Verified via git display name matching GH contributor username -cokemine -dalianmao000 -emozilla -jjovalle99 -kagura-agent -spniyant -olafthiele -r266-tech -xingkongliang -win4r -zhouboli -yongtenglei - -# Nous Research team -benbarclay -jquesnelle - -# GH contributor list verified -spideystreet -dorukardahan -MustafaKara7 -Hmbown -kamil-gwozdz -kira-ariaki -knopki -Unayung -SeeYangZhi -Julientalbot -lesterli -JiayuuWang -tesseracttars-creator -xinbenlv -SaulJWu -angelos -MestreY0d4-Uninter <241404605+MestreY0d4-Uninter@users.noreply.github.com> diff --git a/reference/hermes-agent-2026.5.16/.plans/openai-api-server.md b/reference/hermes-agent-2026.5.16/.plans/openai-api-server.md deleted file mode 100755 index 59038cb..0000000 --- a/reference/hermes-agent-2026.5.16/.plans/openai-api-server.md +++ /dev/null @@ -1,291 +0,0 @@ -# OpenAI-Compatible API Server for Hermes Agent - -## Motivation - -Every major chat frontend (Open WebUI 126k★, LobeChat 73k★, LibreChat 34k★, -AnythingLLM 56k★, NextChat 87k★, ChatBox 39k★, Jan 26k★, HF Chat-UI 8k★, -big-AGI 7k★) connects to backends via the OpenAI-compatible REST API with -SSE streaming. By exposing this endpoint, hermes-agent becomes instantly -usable as a backend for all of them — no custom adapters needed. - -## What It Enables - -``` -┌──────────────────┐ -│ Open WebUI │──┐ -│ LobeChat │ │ POST /v1/chat/completions -│ LibreChat │ ├──► Authorization: Bearer ┌─────────────────┐ -│ AnythingLLM │ │ {"messages": [...]} │ hermes-agent │ -│ NextChat │ │ │ gateway │ -│ Any OAI client │──┘ ◄── SSE streaming response │ (API server) │ -└──────────────────┘ └─────────────────┘ -``` - -A user would: -1. Set `API_SERVER_ENABLED=true` in `~/.hermes/.env` -2. Run `hermes gateway` (API server starts alongside Telegram/Discord/etc.) -3. Point Open WebUI (or any frontend) at `http://localhost:8642/v1` -4. Chat with hermes-agent through any OpenAI-compatible UI - -## Endpoints - -| Method | Path | Purpose | -|--------|------|---------| -| POST | `/v1/chat/completions` | Chat with the agent (streaming + non-streaming) | -| GET | `/v1/models` | List available "models" (returns hermes-agent as a model) | -| GET | `/health` | Health check | - -## Architecture - -### Option A: Gateway Platform Adapter (recommended) - -Create `gateway/platforms/api_server.py` as a new platform adapter that -extends `BasePlatformAdapter`. This is the cleanest approach because: - -- Reuses all gateway infrastructure (session management, auth, context building) -- Runs in the same async loop as other adapters -- Gets message handling, interrupt support, and session persistence for free -- Follows the established pattern (like Telegram, Discord, etc.) -- Uses `aiohttp.web` (already a dependency) for the HTTP server - -The adapter would start an `aiohttp.web.Application` server in `connect()` -and route incoming HTTP requests through the standard `handle_message()` pipeline. - -### Option B: Standalone Component - -A separate HTTP server class in `gateway/api_server.py` that creates its own -AIAgent instances directly. Simpler but duplicates session/auth logic. - -**Recommendation: Option A** — fits the existing architecture, less code to -maintain, gets all gateway features for free. - -## Request/Response Format - -### Chat Completions (non-streaming) - -``` -POST /v1/chat/completions -Authorization: Bearer hermes-api-key-here -Content-Type: application/json - -{ - "model": "hermes-agent", - "messages": [ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "What files are in the current directory?"} - ], - "stream": false, - "temperature": 0.7 -} -``` - -Response: -```json -{ - "id": "chatcmpl-abc123", - "object": "chat.completion", - "created": 1710000000, - "model": "hermes-agent", - "choices": [{ - "index": 0, - "message": { - "role": "assistant", - "content": "Here are the files in the current directory:\n..." - }, - "finish_reason": "stop" - }], - "usage": { - "prompt_tokens": 50, - "completion_tokens": 200, - "total_tokens": 250 - } -} -``` - -### Chat Completions (streaming) - -Same request with `"stream": true`. Response is SSE: - -``` -data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]} - -data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Here "},"finish_reason":null}]} - -data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"are "},"finish_reason":null}]} - -data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} - -data: [DONE] -``` - -### Models List - -``` -GET /v1/models -Authorization: Bearer hermes-api-key-here -``` - -Response: -```json -{ - "object": "list", - "data": [{ - "id": "hermes-agent", - "object": "model", - "created": 1710000000, - "owned_by": "hermes-agent" - }] -} -``` - -## Key Design Decisions - -### 1. Session Management - -The OpenAI API is stateless — each request includes the full conversation. -But hermes-agent sessions have persistent state (memory, skills, tool context). - -**Approach: Hybrid** -- Default: Stateless. Each request is independent. The `messages` array IS - the conversation. No session persistence between requests. -- Opt-in persistent sessions via `X-Session-ID` header. When provided, the - server maintains session state across requests (conversation history, - memory context, tool state). This enables richer agent behavior. -- The session ID also enables interrupt support — a subsequent request with - the same session ID while one is running triggers an interrupt. - -### 2. Streaming - -The agent's `run_conversation()` is synchronous and returns the full response. -For real SSE streaming, we need to emit chunks as they're generated. - -**Phase 1 (MVP):** Run agent in a thread, return the complete response as -a single SSE chunk + `[DONE]`. This works with all frontends — they just see -a fast single-chunk response. Not true streaming but functional. - -**Phase 2:** Add a response callback to AIAgent that emits text chunks as the -LLM generates them. The API server captures these via a queue and streams them -as SSE events. This gives real token-by-token streaming. - -**Phase 3:** Stream tool execution progress too — emit tool call/result events -as the agent works, giving frontends visibility into what the agent is doing. - -### 3. Tool Transparency - -Two modes: -- **Opaque (default):** Frontends see only the final response. Tool calls - happen server-side and are invisible. Best for general-purpose UIs. -- **Transparent (opt-in via header):** Tool calls are emitted as OpenAI-format - tool_call/tool_result messages in the stream. Useful for agent-aware frontends. - -### 4. Authentication - -- Bearer token via `Authorization: Bearer ` header -- Token configured via `API_SERVER_KEY` env var -- Optional: allow unauthenticated local-only access (127.0.0.1 bind) -- Follows the same pattern as other platform adapters - -### 5. Model Mapping - -Frontends send `"model": "hermes-agent"` (or whatever). The actual LLM model -used is configured server-side in config.yaml. The API server maps any -requested model name to the configured hermes-agent model. - -Optionally, allow model passthrough: if the frontend sends -`"model": "anthropic/claude-sonnet-4"`, the agent uses that model. Controlled -by a config flag. - -## Configuration - -```yaml -# In config.yaml -api_server: - enabled: true - port: 8642 - host: "127.0.0.1" # localhost only by default - key: "your-secret-key" # or via API_SERVER_KEY env var - allow_model_override: false # let clients choose the model - max_concurrent: 5 # max simultaneous requests -``` - -Environment variables: -```bash -API_SERVER_ENABLED=true -API_SERVER_PORT=8642 -API_SERVER_HOST=127.0.0.1 -API_SERVER_KEY=your-secret-key -``` - -## Implementation Plan - -### Phase 1: MVP (non-streaming) — PR - -1. `gateway/platforms/api_server.py` — new adapter - - aiohttp.web server with endpoints: - - `POST /v1/chat/completions` — Chat Completions API (universal compat) - - `POST /v1/responses` — Responses API (server-side state, tool preservation) - - `GET /v1/models` — list available models - - `GET /health` — health check - - Bearer token auth middleware - - Non-streaming responses (run agent, return full result) - - Chat Completions: stateless, messages array is the conversation - - Responses API: server-side conversation storage via previous_response_id - - Store full internal conversation (including tool calls) keyed by response ID - - On subsequent requests, reconstruct full context from stored chain - - Frontend system prompt layered on top of hermes-agent's core prompt - -2. `gateway/config.py` — add `Platform.API_SERVER` enum + config - -3. `gateway/run.py` — register adapter in `_create_adapter()` - -4. Tests in `tests/gateway/test_api_server.py` - -### Phase 2: SSE Streaming - -1. Add response streaming to both endpoints - - Chat Completions: `choices[0].delta.content` SSE format - - Responses API: semantic events (response.output_text.delta, etc.) - - Run agent in thread, collect output via callback queue - - Handle client disconnect (cancel agent) - -2. Add `stream_callback` parameter to `AIAgent.run_conversation()` - -### Phase 3: Enhanced Features - -1. Tool call transparency mode (opt-in) -2. Model passthrough/override -3. Concurrent request limiting -4. Usage tracking / rate limiting -5. CORS headers for browser-based frontends -6. GET /v1/responses/{id} — retrieve stored response -7. DELETE /v1/responses/{id} — delete stored response - -## Files Changed - -| File | Change | -|------|--------| -| `gateway/platforms/api_server.py` | NEW — main adapter (~300 lines) | -| `gateway/config.py` | Add Platform.API_SERVER + config (~20 lines) | -| `gateway/run.py` | Register adapter in _create_adapter() (~10 lines) | -| `tests/gateway/test_api_server.py` | NEW — tests (~200 lines) | -| `cli-config.yaml.example` | Add api_server section | -| `README.md` | Mention API server in platform list | - -## Compatibility Matrix - -Once implemented, hermes-agent works as a drop-in backend for: - -| Frontend | Stars | How to Connect | -|----------|-------|---------------| -| Open WebUI | 126k | Settings → Connections → Add OpenAI API, URL: `http://localhost:8642/v1` | -| NextChat | 87k | BASE_URL env var | -| LobeChat | 73k | Custom provider endpoint | -| AnythingLLM | 56k | LLM Provider → Generic OpenAI | -| Oobabooga | 42k | Already a backend, not a frontend | -| ChatBox | 39k | API Host setting | -| LibreChat | 34k | librechat.yaml custom endpoint | -| Chatbot UI | 29k | Custom API endpoint | -| Jan | 26k | Remote model config | -| AionUI | 18k | Custom API endpoint | -| HF Chat-UI | 8k | OPENAI_BASE_URL env var | -| big-AGI | 7k | Custom endpoint | diff --git a/reference/hermes-agent-2026.5.16/.plans/streaming-support.md b/reference/hermes-agent-2026.5.16/.plans/streaming-support.md deleted file mode 100755 index cb4ec11..0000000 --- a/reference/hermes-agent-2026.5.16/.plans/streaming-support.md +++ /dev/null @@ -1,705 +0,0 @@ -# Streaming LLM Response Support for Hermes Agent - -## Overview - -Add token-by-token streaming of LLM responses across all platforms. When enabled, -users see the response typing out live instead of waiting for the full generation. -Streaming is opt-in via config, defaults to off, and all existing non-streaming -code paths remain intact as the default. - -## Design Principles - -1. **Feature-flagged**: `streaming.enabled: true` in config.yaml. Off by default. - When off, all existing code paths are unchanged — zero risk to current behavior. -2. **Callback-based**: A simple `stream_callback(text_delta: str)` function injected - into AIAgent. The agent doesn't know or care what the consumer does with tokens. -3. **Graceful degradation**: If the provider doesn't support streaming, or streaming - fails for any reason, silently fall back to the non-streaming path. -4. **Platform-agnostic core**: The streaming mechanism in AIAgent works the same - regardless of whether the consumer is CLI, Telegram, Discord, or the API server. - ---- - -## Architecture - -``` - stream_callback(delta) - │ - ┌─────────────┐ ┌─────────────▼──────────────┐ - │ LLM API │ │ queue.Queue() │ - │ (stream) │───►│ thread-safe bridge between │ - │ │ │ agent thread & consumer │ - └─────────────┘ └─────────────┬──────────────┘ - │ - ┌──────────────┼──────────────┐ - │ │ │ - ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ - │ CLI │ │ Gateway │ │ API Server│ - │ print to │ │ edit msg │ │ SSE event │ - │ terminal │ │ on Tg/Dc │ │ to client │ - └───────────┘ └───────────┘ └───────────┘ -``` - -The agent runs in a thread. The callback puts tokens into a thread-safe queue. -Each consumer reads the queue in its own context (async task, main thread, etc.). - ---- - -## Configuration - -### config.yaml - -```yaml -streaming: - enabled: false # Master switch. Default off. - # Per-platform overrides (optional): - # cli: true # Override for CLI only - # telegram: true # Override for Telegram only - # discord: false # Keep Discord non-streaming - # api_server: true # Override for API server -``` - -### Environment variables - -``` -HERMES_STREAMING_ENABLED=true # Master switch via env -``` - -### How the flag is read - -- **CLI**: `load_cli_config()` reads `streaming.enabled`, sets env var. AIAgent - checks at init time. -- **Gateway**: `_run_agent()` reads config, decides whether to pass - `stream_callback` to the AIAgent constructor. -- **API server**: For Chat Completions `stream=true` requests, always uses streaming - regardless of config (the client is explicitly requesting it). For non-stream - requests, uses config. - -### Precedence - -1. API server: client's `stream` field overrides everything -2. Per-platform config override (e.g., `streaming.telegram: true`) -3. Master `streaming.enabled` flag -4. Default: off - ---- - -## Implementation Plan - -### Phase 1: Core streaming infrastructure in AIAgent - -**File: run_agent.py** - -#### 1a. Add stream_callback parameter to __init__ (~5 lines) - -```python -def __init__(self, ..., stream_callback: callable = None, ...): - self.stream_callback = stream_callback -``` - -No other init changes. The callback is optional — when None, everything -works exactly as before. - -#### 1b. Add _run_streaming_chat_completion() method (~65 lines) - -New method for Chat Completions API streaming: - -```python -def _run_streaming_chat_completion(self, api_kwargs: dict): - """Stream a chat completion, emitting text tokens via stream_callback. - - Returns a fake response object compatible with the non-streaming code path. - Falls back to non-streaming on any error. - """ - stream_kwargs = dict(api_kwargs) - stream_kwargs["stream"] = True - stream_kwargs["stream_options"] = {"include_usage": True} - - accumulated_content = [] - accumulated_tool_calls = {} # index -> {id, name, arguments} - final_usage = None - - try: - stream = self.client.chat.completions.create(**stream_kwargs) - - for chunk in stream: - if not chunk.choices: - # Usage-only chunk (final) - if chunk.usage: - final_usage = chunk.usage - continue - - delta = chunk.choices[0].delta - - # Text content — emit via callback - if delta.content: - accumulated_content.append(delta.content) - if self.stream_callback: - try: - self.stream_callback(delta.content) - except Exception: - pass - - # Tool call deltas — accumulate silently - if delta.tool_calls: - for tc_delta in delta.tool_calls: - idx = tc_delta.index - if idx not in accumulated_tool_calls: - accumulated_tool_calls[idx] = { - "id": tc_delta.id or "", - "name": "", "arguments": "" - } - if tc_delta.function: - if tc_delta.function.name: - accumulated_tool_calls[idx]["name"] = tc_delta.function.name - if tc_delta.function.arguments: - accumulated_tool_calls[idx]["arguments"] += tc_delta.function.arguments - - # Build fake response compatible with existing code - tool_calls = [] - for idx in sorted(accumulated_tool_calls): - tc = accumulated_tool_calls[idx] - if tc["name"]: - tool_calls.append(SimpleNamespace( - id=tc["id"], type="function", - function=SimpleNamespace(name=tc["name"], arguments=tc["arguments"]), - )) - - return SimpleNamespace( - choices=[SimpleNamespace( - message=SimpleNamespace( - content="".join(accumulated_content) or "", - tool_calls=tool_calls or None, - role="assistant", - ), - finish_reason="tool_calls" if tool_calls else "stop", - )], - usage=final_usage, - model=self.model, - ) - - except Exception as e: - logger.debug("Streaming failed, falling back to non-streaming: %s", e) - return self.client.chat.completions.create(**api_kwargs) -``` - -#### 1c. Modify _run_codex_stream() for Responses API (~10 lines) - -The method already iterates the stream. Add callback emission: - -```python -def _run_codex_stream(self, api_kwargs: dict): - with self.client.responses.stream(**api_kwargs) as stream: - for event in stream: - # Emit text deltas if streaming callback is set - if self.stream_callback and hasattr(event, 'type'): - if event.type == 'response.output_text.delta': - try: - self.stream_callback(event.delta) - except Exception: - pass - return stream.get_final_response() -``` - -#### 1d. Modify _interruptible_api_call() (~5 lines) - -Add the streaming branch: - -```python -def _call(): - try: - if self.api_mode == "codex_responses": - result["response"] = self._run_codex_stream(api_kwargs) - elif self.stream_callback is not None: - result["response"] = self._run_streaming_chat_completion(api_kwargs) - else: - result["response"] = self.client.chat.completions.create(**api_kwargs) - except Exception as e: - result["error"] = e -``` - -#### 1e. Signal end-of-stream to consumers (~5 lines) - -After the API call returns, signal the callback that streaming is done -so consumers can finalize (remove cursor, close SSE, etc.): - -```python -# In run_conversation(), after _interruptible_api_call returns: -if self.stream_callback: - try: - self.stream_callback(None) # None = end of stream signal - except Exception: - pass -``` - -Consumers check: `if delta is None: finalize()` - -**Tests for Phase 1:** (~150 lines) -- Test _run_streaming_chat_completion with mocked stream -- Test fallback to non-streaming on error -- Test tool_call accumulation during streaming -- Test stream_callback receives correct deltas -- Test None signal at end of stream -- Test streaming disabled when callback is None - ---- - -### Phase 2: Gateway consumers (Telegram, Discord, etc.) - -**File: gateway/run.py** - -#### 2a. Read streaming config (~15 lines) - -In `_run_agent()`, before creating the AIAgent: - -```python -# Read streaming config -_streaming_enabled = False -try: - # Check per-platform override first - platform_key = source.platform.value if source.platform else "" - _stream_cfg = {} # loaded from config.yaml streaming section - if _stream_cfg.get(platform_key) is not None: - _streaming_enabled = bool(_stream_cfg[platform_key]) - else: - _streaming_enabled = bool(_stream_cfg.get("enabled", False)) -except Exception: - pass -# Env var override -if os.getenv("HERMES_STREAMING_ENABLED", "").lower() in ("true", "1", "yes"): - _streaming_enabled = True -``` - -#### 2b. Set up queue + callback (~15 lines) - -```python -_stream_q = None -_stream_done = None -_stream_msg_id = [None] # mutable ref for the async task - -if _streaming_enabled: - import queue as _q - _stream_q = _q.Queue() - _stream_done = threading.Event() - - def _on_token(delta): - if delta is None: - _stream_done.set() - else: - _stream_q.put(delta) -``` - -Pass `stream_callback=_on_token` to the AIAgent constructor. - -#### 2c. Telegram/Discord stream preview task (~50 lines) - -```python -async def stream_preview(): - """Progressively edit a message with streaming tokens.""" - if not _stream_q: - return - adapter = self.adapters.get(source.platform) - if not adapter: - return - - accumulated = [] - token_count = 0 - last_edit = 0.0 - MIN_TOKENS = 20 # Don't show until enough context - EDIT_INTERVAL = 1.5 # Respect Telegram rate limits - - try: - while not _stream_done.is_set(): - try: - chunk = _stream_q.get(timeout=0.1) - accumulated.append(chunk) - token_count += 1 - except queue.Empty: - continue - - now = time.monotonic() - if token_count >= MIN_TOKENS and (now - last_edit) >= EDIT_INTERVAL: - preview = "".join(accumulated) + " ▌" - if _stream_msg_id[0] is None: - r = await adapter.send( - chat_id=source.chat_id, - content=preview, - metadata=_thread_metadata, - ) - if r.success and r.message_id: - _stream_msg_id[0] = r.message_id - else: - await adapter.edit_message( - chat_id=source.chat_id, - message_id=_stream_msg_id[0], - content=preview, - ) - last_edit = now - - # Drain remaining tokens - while not _stream_q.empty(): - accumulated.append(_stream_q.get_nowait()) - - # Final edit — remove cursor, show complete text - if _stream_msg_id[0] and accumulated: - await adapter.edit_message( - chat_id=source.chat_id, - message_id=_stream_msg_id[0], - content="".join(accumulated), - ) - - except asyncio.CancelledError: - # Clean up on cancel - if _stream_msg_id[0] and accumulated: - try: - await adapter.edit_message( - chat_id=source.chat_id, - message_id=_stream_msg_id[0], - content="".join(accumulated), - ) - except Exception: - pass - except Exception as e: - logger.debug("stream_preview error: %s", e) -``` - -#### 2d. Skip final send if already streamed (~10 lines) - -In `_process_message_background()` (base.py), after getting the response, -if streaming was active and `_stream_msg_id[0]` is set, the final response -was already delivered via progressive edits. Skip the normal `self.send()` -call to avoid duplicating the message. - -This is the most delicate integration point — we need to communicate from -the gateway's `_run_agent` back to the base adapter's response sender that -the response was already delivered. Options: - -- **Option A**: Return a special marker in the result dict: - `result["_streamed_msg_id"] = _stream_msg_id[0]` - The base adapter checks this and skips `send()`. - -- **Option B**: Edit the already-sent message with the final response - (which may differ slightly from accumulated tokens due to think-block - stripping, etc.) and don't send a new one. - -- **Option C**: The stream preview task handles the FULL final response - (including any post-processing), and the handler returns None to skip - the normal send path. - -Recommended: **Option A** — cleanest separation. The result dict already -carries metadata; adding one more field is low-risk. - -**Platform-specific considerations:** - -| Platform | Edit support | Rate limits | Streaming approach | -|----------|-------------|-------------|-------------------| -| Telegram | ✅ edit_message_text | ~20 edits/min | Edit every 1.5s | -| Discord | ✅ message.edit | 5 edits/5s per message | Edit every 1.2s | -| Slack | ✅ chat.update | Tier 3 (~50/min) | Edit every 1.5s | -| WhatsApp | ❌ no edit support | N/A | Skip streaming, use normal path | -| HomeAssistant | ❌ no edit | N/A | Skip streaming | -| API Server | ✅ SSE native | No limit | Real SSE events | - -WhatsApp and HomeAssistant fall back to non-streaming automatically because -they don't support message editing. - -**Tests for Phase 2:** (~100 lines) -- Test stream_preview sends/edits correctly -- Test skip-final-send when streaming delivered -- Test WhatsApp/HA graceful fallback -- Test streaming disabled per-platform config -- Test thread_id metadata forwarded in stream messages - ---- - -### Phase 3: CLI streaming - -**File: cli.py** - -#### 3a. Set up callback in the CLI chat loop (~20 lines) - -In `_chat_once()` or wherever the agent is invoked: - -```python -if streaming_enabled: - _stream_q = queue.Queue() - _stream_done = threading.Event() - - def _cli_stream_callback(delta): - if delta is None: - _stream_done.set() - else: - _stream_q.put(delta) - - agent.stream_callback = _cli_stream_callback -``` - -#### 3b. Token display thread/task (~30 lines) - -Start a thread that reads the queue and prints tokens: - -```python -def _stream_display(): - """Print tokens to terminal as they arrive.""" - first_token = True - while not _stream_done.is_set(): - try: - delta = _stream_q.get(timeout=0.1) - except queue.Empty: - continue - if first_token: - # Print response box top border - _cprint(f"\n{top}") - first_token = False - sys.stdout.write(delta) - sys.stdout.flush() - # Drain remaining - while not _stream_q.empty(): - sys.stdout.write(_stream_q.get_nowait()) - sys.stdout.flush() - # Print bottom border - _cprint(f"\n\n{bot}") -``` - -**Integration challenge: prompt_toolkit** - -The CLI uses prompt_toolkit which controls the terminal. Writing directly -to stdout while prompt_toolkit is active can cause display corruption. -The existing KawaiiSpinner already solves this by using prompt_toolkit's -`patch_stdout` context. The streaming display would need to do the same. - -Alternative: use `_cprint()` for each token chunk (routes through -prompt_toolkit's renderer). But this might be slow for individual tokens. - -Recommended approach: accumulate tokens in small batches (e.g., every 50ms) -and `_cprint()` the batch. This balances display responsiveness with -prompt_toolkit compatibility. - -**Tests for Phase 3:** (~50 lines) -- Test CLI streaming callback setup -- Test response box borders with streaming -- Test fallback when streaming disabled - ---- - -### Phase 4: API Server real streaming - -**File: gateway/platforms/api_server.py** - -Replace the pseudo-streaming `_write_sse_chat_completion()` with real -token-by-token SSE when the agent supports it. - -#### 4a. Wire streaming callback for stream=true requests (~20 lines) - -```python -if stream: - _stream_q = queue.Queue() - - def _api_stream_callback(delta): - _stream_q.put(delta) # None = done - - # Pass callback to _run_agent - result, usage = await self._run_agent( - ..., stream_callback=_api_stream_callback, - ) -``` - -#### 4b. Real SSE writer (~40 lines) - -```python -async def _write_real_sse(self, request, completion_id, model, stream_q): - response = web.StreamResponse( - headers={"Content-Type": "text/event-stream", "Cache-Control": "no-cache"}, - ) - await response.prepare(request) - - # Role chunk - await response.write(...) - - # Stream content chunks as they arrive - while True: - try: - delta = await asyncio.get_event_loop().run_in_executor( - None, lambda: stream_q.get(timeout=0.1) - ) - except queue.Empty: - continue - - if delta is None: # End of stream - break - - chunk = {"id": completion_id, "object": "chat.completion.chunk", ... - "choices": [{"delta": {"content": delta}, ...}]} - await response.write(f"data: {json.dumps(chunk)}\n\n".encode()) - - # Finish + [DONE] - await response.write(...) - await response.write(b"data: [DONE]\n\n") - return response -``` - -**Challenge: concurrent execution** - -The agent runs in a thread executor. SSE writing happens in the async event -loop. The queue bridges them. But `_run_agent()` currently awaits the full -result before returning. For real streaming, we need to start the agent in -the background and stream tokens while it runs: - -```python -# Start agent in background -agent_task = asyncio.create_task(self._run_agent_async(...)) - -# Stream tokens while agent runs -await self._write_real_sse(request, ..., stream_q) - -# Agent is done by now (stream_q received None) -result, usage = await agent_task -``` - -This requires splitting `_run_agent` into an async version that doesn't -block waiting for the result, or running it in a separate task. - -**Responses API SSE format:** - -For `/v1/responses` with `stream=true`, the SSE events are different: - -``` -event: response.output_text.delta -data: {"type":"response.output_text.delta","delta":"Hello"} - -event: response.completed -data: {"type":"response.completed","response":{...}} -``` - -This needs a separate SSE writer that emits Responses API format events. - -**Tests for Phase 4:** (~80 lines) -- Test real SSE streaming with mocked agent -- Test SSE event format (Chat Completions vs Responses) -- Test client disconnect during streaming -- Test fallback to pseudo-streaming when callback not available - ---- - -## Integration Issues & Edge Cases - -### 1. Tool calls during streaming - -When the model returns tool calls instead of text, no text tokens are emitted. -The stream_callback is simply never called with text. After tools execute, the -next API call may produce the final text response — streaming picks up again. - -The stream preview task needs to handle this: if no tokens arrive during a -tool-call round, don't send/edit any message. The tool progress messages -continue working as before. - -### 2. Duplicate messages - -The biggest risk: the agent sends the final response normally (via the -existing send path) AND the stream preview already showed it. The user -sees the response twice. - -Prevention: when streaming is active and tokens were delivered, the final -response send must be suppressed. The `result["_streamed_msg_id"]` marker -tells the base adapter to skip its normal send. - -### 3. Response post-processing - -The final response may differ from the accumulated streamed tokens: -- Think block stripping (`...` removed) -- Trailing whitespace cleanup -- Tool result media tag appending - -The stream preview shows raw tokens. The final edit should use the -post-processed version. This means the final edit (removing the cursor) -should use the post-processed `final_response`, not just the accumulated -stream text. - -### 4. Context compression during streaming - -If the agent triggers context compression mid-conversation, the streaming -tokens from BEFORE compression are from a different context than those -after. This isn't a problem in practice — compression happens between -API calls, not during streaming. - -### 5. Interrupt during streaming - -User sends a new message while streaming → interrupt. The stream is killed -(HTTP connection closed), accumulated tokens are shown as-is (no cursor), -and the interrupt message is processed normally. This is already handled by -`_interruptible_api_call` closing the client. - -### 6. Multi-model / fallback - -If the primary model fails and the agent falls back to a different model, -streaming state resets. The fallback call may or may not support streaming. -The graceful fallback in `_run_streaming_chat_completion` handles this. - -### 7. Rate limiting on edits - -Telegram: ~20 edits/minute (~1 every 3 seconds to be safe) -Discord: 5 edits per 5 seconds per message -Slack: ~50 API calls/minute - -The 1.5s edit interval is conservative enough for all platforms. If we get -429 rate limit errors on edits, just skip that edit cycle and try next time. - ---- - -## Files Changed Summary - -| File | Phase | Changes | -|------|-------|---------| -| `run_agent.py` | 1 | +stream_callback param, +_run_streaming_chat_completion(), modify _run_codex_stream(), modify _interruptible_api_call() | -| `gateway/run.py` | 2 | +streaming config reader, +queue/callback setup, +stream_preview task, +skip-final-send logic | -| `gateway/platforms/base.py` | 2 | +check for _streamed_msg_id in response handler | -| `cli.py` | 3 | +streaming setup, +token display, +response box integration | -| `gateway/platforms/api_server.py` | 4 | +real SSE writer, +streaming callback wiring | -| `hermes_cli/config.py` | 1 | +streaming config defaults | -| `cli-config.yaml.example` | 1 | +streaming section | -| `tests/test_streaming.py` | 1-4 | NEW — ~380 lines of tests | - -**Total new code**: ~500 lines across all phases -**Total test code**: ~380 lines - ---- - -## Rollout Plan - -1. **Phase 1** (core): Merge to main. Streaming disabled by default. - Zero impact on existing behavior. Can be tested with env var. - -2. **Phase 2** (gateway): Merge to main. Test on Telegram manually. - Enable per-platform: `streaming.telegram: true` in config. - -3. **Phase 3** (CLI): Merge to main. Test in terminal. - Enable: `streaming.cli: true` or `streaming.enabled: true`. - -4. **Phase 4** (API server): Merge to main. Test with Open WebUI. - Auto-enabled when client sends `stream: true`. - -Each phase is independently mergeable and testable. Streaming stays -off by default throughout. Once all phases are stable, consider -changing the default to enabled. - ---- - -## Config Reference (final state) - -```yaml -# config.yaml -streaming: - enabled: false # Master switch (default: off) - cli: true # Per-platform override - telegram: true - discord: true - slack: true - api_server: true # API server always streams when client requests it - edit_interval: 1.5 # Seconds between message edits (default: 1.5) - min_tokens: 20 # Tokens before first display (default: 20) -``` - -```bash -# Environment variable override -HERMES_STREAMING_ENABLED=true -``` diff --git a/reference/openai-codex/.bazelignore b/reference/openai-codex/.bazelignore deleted file mode 100644 index 2e90753..0000000 --- a/reference/openai-codex/.bazelignore +++ /dev/null @@ -1,4 +0,0 @@ -# Without this, Bazel will consider BUILD.bazel files in -# .git/sl/origbackups (which can be populated by Sapling SCM). -.git -codex-rs/target diff --git a/reference/openai-codex/.bazelrc b/reference/openai-codex/.bazelrc deleted file mode 100644 index 6357eb8..0000000 --- a/reference/openai-codex/.bazelrc +++ /dev/null @@ -1,202 +0,0 @@ -common --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 -common --repo_env=BAZEL_NO_APPLE_CPP_TOOLCHAIN=1 -# Dummy xcode config so we don't need to build xcode_locator in repo rule. -common --xcode_version_config=//:disable_xcode - -common --disk_cache=~/.cache/bazel-disk-cache -common --repo_contents_cache=~/.cache/bazel-repo-contents-cache -common --repository_cache=~/.cache/bazel-repo-cache -common --remote_cache_compression -startup --experimental_remote_repo_contents_cache - -common --experimental_platform_in_output_dir - -# Runfiles strategy rationale: codex-rs/utils/cargo-bin/README.md -common --noenable_runfiles - -common --enable_platform_specific_config -common:linux --host_platform=//:local_linux -common:windows --host_platform=//:local_windows -common --@rules_cc//cc/toolchains/args/archiver_flags:use_libtool_on_macos=False -common --@llvm//config:experimental_stub_libgcc_s - -# TODO(zbarsky): rules_rust doesn't implement this flag properly with remote exec... -# common --@rules_rust//rust/settings:pipelined_compilation - -common --incompatible_strict_action_env -# Not ideal, but We need to allow dotslash to be found -common:linux --test_env=PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin -common:macos --test_env=PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin - -# Pass through some env vars Windows needs to use powershell? -common:windows --test_env=SYSTEMROOT -common:windows --test_env=COMSPEC -common:windows --test_env=WINDIR -# Rust's libtest harness runs test bodies on std-spawned threads. The default -# 2 MiB stack can be too small for large async test futures on Windows CI; see -# https://github.com/openai/codex/pull/19067 for the motivating failure. -common --test_env=RUST_MIN_STACK=8388608 # 8 MiB - -common --test_output=errors -common --bes_results_url=https://app.buildbuddy.io/invocation/ -common --bes_backend=grpcs://remote.buildbuddy.io -common --remote_cache=grpcs://remote.buildbuddy.io -common --remote_download_toplevel -common --nobuild_runfile_links -common --remote_timeout=3600 -common --noexperimental_throttle_remote_action_building -common --experimental_remote_execution_keepalive -common --grpc_keepalive_time=30s -common --experimental_remote_downloader=grpcs://remote.buildbuddy.io - -# This limits both in-flight executions and concurrent downloads. Even with high number -# of jobs execution will still be limited by CPU cores, so this just pays a bit of -# memory in exchange for higher download concurrency. -common --jobs=30 - -common:remote --extra_execution_platforms=//:rbe -common:remote --remote_executor=grpcs://remote.buildbuddy.io -common:remote --jobs=800 -# TODO(team): Evaluate if this actually helps, zbarsky is not sure, everything seems bottlenecked on `core` either way. -# Enable pipelined compilation since we are not bound by local CPU count. -#common:remote --@rules_rust//rust/settings:pipelined_compilation - -# GitHub Actions CI configs. -common:ci --remote_download_minimal -common:ci --keep_going -common:ci --verbose_failures -common:ci --build_metadata=REPO_URL=https://github.com/openai/codex.git -common:ci --build_metadata=ROLE=CI -common:ci --build_metadata=VISIBILITY=PUBLIC -# rules_rust derives debug level from Bazel toolchain/compilation-mode settings, -# not Cargo profiles. Keep CI Rust actions explicit and lean. -common:ci --@rules_rust//rust/settings:extra_rustc_flag=-Cdebuginfo=0 -common:ci --@rules_rust//rust/settings:extra_exec_rustc_flag=-Cdebuginfo=0 - -# Disable disk cache in CI since we have a remote one and aren't using persistent workers. -common:ci --disk_cache= - -# Shared config for the main Bazel CI workflow. -common:ci-bazel --config=ci -common:ci-bazel --build_metadata=TAG_workflow=bazel -# Bazel CI cross-compiles in several legs, and the V8-backed code-mode tests -# are not stable in that setup yet. Keep running the rest of the Rust -# integration suites through the workspace-root launcher. -common:ci-bazel --test_env=CODEX_BAZEL_TEST_SKIP_FILTERS=suite::code_mode:: - -# Shared config for Bazel-backed Rust linting. -build:clippy --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect -build:clippy --output_groups=+clippy_checks -build:clippy --@rules_rust//rust/settings:clippy.toml=//codex-rs:clippy.toml -# Keep this deny-list in sync with `codex-rs/Cargo.toml` `[workspace.lints.clippy]`. -# Cargo applies those lint levels to member crates that opt into `[lints] workspace = true` -# in their own `Cargo.toml`, but `rules_rust` Bazel clippy does not read Cargo lint levels. -# `clippy.toml` can configure lint behavior, but it cannot set allow/warn/deny/forbid levels. -build:clippy --@rules_rust//rust/settings:clippy_flag=-Dwarnings -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::await_holding_invalid_type -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::await_holding_lock -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::expect_used -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::identity_op -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_clamp -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_filter -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_find -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_flatten -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_map -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_memcpy -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_non_exhaustive -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_ok_or -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_range_contains -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_retain -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_strip -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_try_fold -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::manual_unwrap_or -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::needless_borrow -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::needless_borrowed_reference -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::needless_collect -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::needless_late_init -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::needless_option_as_deref -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::needless_question_mark -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::needless_update -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::redundant_clone -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::redundant_closure -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::redundant_closure_for_method_calls -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::redundant_static_lifetimes -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::trivially_copy_pass_by_ref -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::uninlined_format_args -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::unnecessary_filter_map -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::unnecessary_lazy_evaluations -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::unnecessary_sort_by -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::unnecessary_to_owned -build:clippy --@rules_rust//rust/settings:clippy_flag=--deny=clippy::unwrap_used - -# Shared config for Bazel-backed argument-comment-lint. -build:argument-comment-lint --aspects=//tools/argument-comment-lint:lint_aspect.bzl%rust_argument_comment_lint_aspect -build:argument-comment-lint --output_groups=argument_comment_lint_checks -build:argument-comment-lint --@rules_rust//rust/toolchain/channel=nightly - -# Rearrange caches on Windows so they're on the same volume as the checkout. -common:ci-windows --config=ci-bazel -common:ci-windows --build_metadata=TAG_os=windows -common:ci-windows --repo_contents_cache=D:/a/.cache/bazel-repo-contents-cache - -# We prefer to run the build actions entirely remotely so we can dial up the concurrency. -# We have platform-specific tests, so we want to execute the tests on all platforms using the strongest sandboxing available on each platform. - -# On linux, we can do a full remote build/test, by targeting the right (x86/arm) runners, so we have coverage of both. -# Linux crossbuilds don't work until we untangle the libc constraint mess. -common:ci-linux --config=ci-bazel -common:ci-linux --build_metadata=TAG_os=linux -common:ci-linux --config=remote -common:ci-linux --strategy=remote -common:ci-linux --platforms=//:rbe - -# On mac, we can run all the build actions remotely but test actions locally. -common:ci-macos --config=ci-bazel -common:ci-macos --build_metadata=TAG_os=macos -common:ci-macos --config=remote -common:ci-macos --strategy=remote -common:ci-macos --strategy=TestRunner=darwin-sandbox,local - -# On Windows, use Linux remote execution for build actions but keep test actions -# on the Windows runner so Bazel's normal test sharding and flaky-test retries -# still run against Windows binaries. -common:ci-windows-cross --config=ci-windows -common:ci-windows-cross --build_metadata=TAG_windows_cross_compile=true -common:ci-windows-cross --config=remote -common:ci-windows-cross --host_platform=//:rbe -common:ci-windows-cross --strategy=remote -common:ci-windows-cross --strategy=TestRunner=local -common:ci-windows-cross --local_test_jobs=4 -common:ci-windows-cross --test_env=RUST_TEST_THREADS=1 -# Native Windows CI still covers the PowerShell tests. The cross-built gnullvm -# binaries currently hang in PowerShell AST parser tests when those binaries are -# run on the Windows runner. -common:ci-windows-cross --test_env=CODEX_BAZEL_TEST_SKIP_FILTERS=suite::code_mode::,powershell -common:ci-windows-cross --platforms=//:windows_x86_64_gnullvm -common:ci-windows-cross --extra_execution_platforms=//:rbe,//:windows_x86_64_msvc -common:ci-windows-cross --extra_toolchains=//:windows_gnullvm_tests_on_msvc_host_toolchain - -# Linux-only V8 CI config. -common:ci-v8 --config=ci -common:ci-v8 --build_metadata=TAG_workflow=v8 -common:ci-v8 --build_metadata=TAG_os=linux -common:ci-v8 --config=remote -common:ci-v8 --strategy=remote - -# Source-built Bazel V8 artifacts use the in-process sandbox by default. This -# does not affect Cargo's default prebuilt rusty_v8 path. -common --@v8//:v8_enable_pointer_compression=True -common --@v8//:v8_enable_sandbox=True - -# Keep currently published rusty_v8 release artifacts non-sandboxed until the -# artifact migration ships matching Rust feature selection for Cargo consumers. -common:v8-release-compat --@v8//:v8_enable_pointer_compression=False -common:v8-release-compat --@v8//:v8_enable_sandbox=False - -# Match rusty_v8's upstream GN release contract for published artifacts: every -# target object uses Chromium's custom libc++ headers and the archive folds in -# the matching runtime objects. -common:rusty-v8-upstream-libcxx --@v8//:v8_use_rusty_v8_custom_libcxx=True - -# Optional per-user local overrides. -try-import %workspace%/user.bazelrc diff --git a/reference/openai-codex/.bazelversion b/reference/openai-codex/.bazelversion deleted file mode 100644 index f7ee066..0000000 --- a/reference/openai-codex/.bazelversion +++ /dev/null @@ -1 +0,0 @@ -9.0.0 diff --git a/reference/openai-codex/.codespellignore b/reference/openai-codex/.codespellignore deleted file mode 100644 index 23924fe..0000000 --- a/reference/openai-codex/.codespellignore +++ /dev/null @@ -1,6 +0,0 @@ -iTerm -iTerm2 -psuedo -SOM -te -TE diff --git a/reference/openai-codex/.codespellrc b/reference/openai-codex/.codespellrc deleted file mode 100644 index 838b7e8..0000000 --- a/reference/openai-codex/.codespellrc +++ /dev/null @@ -1,6 +0,0 @@ -[codespell] -# Ref: https://github.com/codespell-project/codespell#using-a-config-file -skip = .git*,vendor,*-lock.yaml,*.lock,.codespellrc,*test.ts,*.jsonl,frame*.txt,*.snap,*.snap.new -check-hidden = true -ignore-regex = ^\s*"image/\S+": ".*|\b(afterAll)\b -ignore-words-list = ratatui,ser,iTerm,iterm2,iterm,te,TE,PASE,SEH diff --git a/reference/openai-codex/.codex/environments/environment.toml b/reference/openai-codex/.codex/environments/environment.toml deleted file mode 100644 index f67f198..0000000 --- a/reference/openai-codex/.codex/environments/environment.toml +++ /dev/null @@ -1,11 +0,0 @@ -# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY -version = 1 -name = "codex" - -[setup] -script = "" - -[[actions]] -name = "Run" -icon = "run" -command = "cargo +1.93.0 run --manifest-path=codex-rs/Cargo.toml --bin codex -- -c mcp_oauth_credentials_store=file" diff --git a/reference/openai-codex/.codex/skills/babysit-pr/SKILL.md b/reference/openai-codex/.codex/skills/babysit-pr/SKILL.md deleted file mode 100644 index 1b95144..0000000 --- a/reference/openai-codex/.codex/skills/babysit-pr/SKILL.md +++ /dev/null @@ -1,194 +0,0 @@ ---- -name: babysit-pr -description: Babysit a GitHub pull request after creation by continuously polling review comments, CI checks/workflow runs, and mergeability state until the PR is merged/closed or user help is required. Diagnose failures, retry likely flaky failures up to 3 times, auto-fix/push branch-related issues when appropriate, and keep watching open PRs so fresh review feedback is surfaced promptly. Use when the user asks Codex to monitor a PR, watch CI, handle review comments, or keep an eye on failures and feedback on an open PR. ---- - -# PR Babysitter - -## Objective -Babysit a PR persistently until one of these terminal outcomes occurs: - -- The PR is merged or closed. -- A situation requires user help (for example CI infrastructure issues, repeated flaky failures after retry budget is exhausted, permission problems, or ambiguity that cannot be resolved safely). -- Optional handoff milestone: the PR is currently green + mergeable + review-clean. Treat this as a progress state, not a watcher stop, so late-arriving review comments are still surfaced promptly while the PR remains open. - -Do not stop merely because a single snapshot returns `idle` while checks are still pending. - -## Inputs -Accept any of the following: - -- No PR argument: infer the PR from the current branch (`--pr auto`) -- PR number -- PR URL - -## Core Workflow - -1. When the user asks to "monitor"/"watch"/"babysit" a PR, start with the watcher's continuous mode (`--watch`) unless you are intentionally doing a one-shot diagnostic snapshot. -2. Run the watcher script to snapshot PR/review/CI state (or consume each streamed snapshot from `--watch`). -3. Inspect the `actions` list in the JSON response. -4. If `diagnose_ci_failure` is present, inspect failed run logs and classify the failure. -5. If the failure is likely caused by the current branch, patch code locally, commit, and push. Do not patch random flaky tests, CI infrastructure, dependency outages, runner issues, or other failures that are unrelated to the branch. -6. If `process_review_comment` is present, inspect surfaced review items and decide whether to address them. -7. If a review item is actionable and correct, patch code locally, commit, push, and then mark the associated review thread/comment as resolved once the fix is on GitHub. -8. Do not post replies to human-authored review comments/threads unless the user explicitly confirms the exact response. If a human review item is non-actionable, already addressed, or not valid, surface the item and recommended response to the user instead of replying on GitHub. -9. If the failure is likely flaky/unrelated and `retry_failed_checks` is present, rerun failed jobs with `--retry-failed-now`. -10. If both actionable review feedback and `retry_failed_checks` are present, prioritize review feedback first; a new commit will retrigger CI, so avoid rerunning flaky checks on the old SHA unless you intentionally defer the review change. -11. On every loop, look for newly surfaced review feedback before acting on CI failures or mergeability state, then verify mergeability / merge-conflict status (for example via `gh pr view`) alongside CI. -12. After any push or rerun action, immediately return to step 1 and continue polling on the updated SHA/state. -13. If you had been using `--watch` before pausing to patch/commit/push, relaunch `--watch` yourself in the same turn immediately after the push (do not wait for the user to re-invoke the skill). -14. Repeat polling until `stop_pr_closed` appears or a user-help-required blocker is reached. A green + review-clean + mergeable PR is a progress milestone, not a reason to stop the watcher while the PR is still open. -15. Maintain terminal/session ownership: while babysitting is active, keep consuming watcher output in the same turn; do not leave a detached `--watch` process running and then end the turn as if monitoring were complete. - -## Commands - -### One-shot snapshot - -```bash -python3 .codex/skills/babysit-pr/scripts/gh_pr_watch.py --pr auto --once -``` - -### Continuous watch (JSONL) - -```bash -python3 .codex/skills/babysit-pr/scripts/gh_pr_watch.py --pr auto --watch -``` - -### Trigger flaky retry cycle (only when watcher indicates) - -```bash -python3 .codex/skills/babysit-pr/scripts/gh_pr_watch.py --pr auto --retry-failed-now -``` - -### Explicit PR target - -```bash -python3 .codex/skills/babysit-pr/scripts/gh_pr_watch.py --pr --once -``` - -## CI Failure Classification -Use `gh` commands to inspect failed runs before deciding to rerun. - -- `gh run view --json jobs,name,workflowName,conclusion,status,url,headSha` -- `gh api repos///actions/runs//jobs -X GET -f per_page=100` -- `gh api repos///actions/jobs//logs > /tmp/codex-gh-job--logs.zip` -- `gh run view --log-failed` as a fallback after the overall workflow run is complete - -`gh run view --log-failed` is workflow-run scoped and may not expose failed-job logs until the overall run finishes. For faster diagnosis, poll the run's jobs first and, as soon as a specific job has failed, fetch that job's logs directly from the Actions job logs endpoint. The watcher includes a `failed_jobs` list with each failed job's `job_id` and `logs_endpoint` when GitHub exposes one. - -Prefer treating failures as branch-related when failed-job logs point to changed code (compile/test/lint/typecheck/snapshots/static analysis in touched areas). - -Prefer treating failures as flaky/unrelated when logs show transient infra/external issues (timeouts, runner provisioning failures, registry/network outages, GitHub Actions infra errors). - -Do not attempt to fix flaky/unrelated failures by changing tests, build scripts, CI configuration, dependency pins, or infrastructure-adjacent code unless the logs clearly connect the failure to the PR branch. For flaky/unrelated failures, rerun only when the watcher recommends `retry_failed_checks`; otherwise wait or stop for user help. - -If classification is ambiguous, perform one manual diagnosis attempt before choosing rerun. - -Read `.codex/skills/babysit-pr/references/heuristics.md` for a concise checklist. - -## Review Comment Handling -The watcher surfaces review items from: - -- PR issue comments -- Inline review comments -- Review submissions (COMMENT / APPROVED / CHANGES_REQUESTED) - -It intentionally surfaces Codex reviewer bot feedback (for example comments/reviews from `chatgpt-codex-connector[bot]`) in addition to human reviewer feedback. Most unrelated bot noise should still be ignored. -For safety, the watcher only auto-surfaces trusted human review authors (for example repo OWNER/MEMBER/COLLABORATOR, plus the authenticated operator) and approved review bots such as Codex. -On a fresh watcher state file, existing pending review feedback may be surfaced immediately (not only comments that arrive after monitoring starts). This is intentional so already-open review comments are not missed. - -When you agree with a comment and it is actionable: - -1. Patch code locally. -2. Commit with `codex: address PR review feedback (#)`. -3. Push to the PR head branch. -4. After the push succeeds, mark the associated GitHub review thread/comment as resolved. -5. Resume watching on the new SHA immediately (do not stop after reporting the push). -6. If monitoring was running in `--watch` mode, restart `--watch` immediately after the push in the same turn; do not wait for the user to ask again. - -Do not post replies to human-authored GitHub review comments/threads automatically. If you disagree with a human comment, believe it is non-actionable/already addressed, or need to answer a question, report the item to the user with a suggested response and wait for explicit confirmation before posting anything on GitHub. If the user approves a response, prefix it with `[codex]` so it is clear the response is automated and not from the human user. -If the watcher later surfaces your own approved reply because the authenticated operator is treated as a trusted review author, treat that self-authored item as already handled and do not reply again. -If a code review comment/thread is already marked as resolved in GitHub, treat it as non-actionable and safely ignore it unless new unresolved follow-up feedback appears. - -## Git Safety Rules - -- Work only on the PR head branch. -- Avoid destructive git commands. -- Do not switch branches unless necessary to recover context. -- Before editing, check for unrelated uncommitted changes. If present, stop and ask the user. -- After each successful fix, commit and `git push`, then re-run the watcher. -- If you interrupted a live `--watch` session to make the fix, restart `--watch` immediately after the push in the same turn. -- Do not run multiple concurrent `--watch` processes for the same PR/state file; keep one watcher session active and reuse it until it stops or you intentionally restart it. -- A push is not a terminal outcome; continue the monitoring loop unless a strict stop condition is met. - -Commit message defaults: - -- `codex: fix CI failure on PR #` -- `codex: address PR review feedback (#)` - -## Monitoring Loop Pattern -Use this loop in a live Codex session: - -1. Run `--once`. -2. Read `actions`. -3. First check whether the PR is now merged or otherwise closed; if so, report that terminal state and stop polling immediately. -4. Check CI summary, new review items, and mergeability/conflict status. -5. Diagnose CI failures and classify branch-related vs flaky/unrelated. If the overall run is still pending but `failed_jobs` already includes a failed job, fetch that job's logs and diagnose immediately instead of waiting for the whole workflow run to finish. Patch only when the failure is branch-related. -6. For each surfaced review item from another author, patch/commit/push and then resolve it if it is actionable. If it is non-actionable, already addressed, or requires a written answer, surface it to the user with a suggested response instead of posting automatically. If a later snapshot surfaces your own approved reply, treat it as informational and continue without responding again. -7. Process actionable review comments before flaky reruns when both are present; if a review fix requires a commit, push it and skip rerunning failed checks on the old SHA. -8. Retry failed checks only when `retry_failed_checks` is present and you are not about to replace the current SHA with a review/CI fix commit. Do not make code changes for unrelated flakes or infrastructure failures just to get CI green. -9. If you pushed a commit, resolved a review thread, or triggered a rerun, report the action briefly and continue polling (do not stop). If a human review comment needs a written GitHub response, stop and ask for confirmation before posting. -10. After a review-fix push, proactively restart continuous monitoring (`--watch`) in the same turn unless a strict stop condition has already been reached. -11. If everything is passing, mergeable, not blocked on required review approval, and there are no unaddressed review items, report that the PR is currently ready to merge but keep the watcher running so new review comments are surfaced quickly while the PR remains open. -12. If blocked on a user-help-required issue (infra outage, exhausted flaky retries, unclear reviewer request, permissions), report the blocker and stop. -13. Otherwise sleep according to the polling cadence below and repeat. - -When the user explicitly asks to monitor/watch/babysit a PR, prefer `--watch` so polling continues autonomously in one command. Use repeated `--once` snapshots only for debugging, local testing, or when the user explicitly asks for a one-shot check. -Do not stop to ask the user whether to continue polling; continue autonomously until a strict stop condition is met or the user explicitly interrupts. -Do not hand control back to the user after a review-fix push just because a new SHA was created; restarting the watcher and re-entering the poll loop is part of the same babysitting task. -If a `--watch` process is still running and no strict stop condition has been reached, the babysitting task is still in progress; keep streaming/consuming watcher output instead of ending the turn. - -## Polling Cadence -Keep review polling aggressive and continue monitoring even after CI turns green: - -- While CI is not green (pending/running/queued or failing): poll every 1 minute. -- After CI turns green: keep polling at the base cadence while the PR remains open so newly posted review comments are surfaced promptly instead of waiting on a long green-state backoff. -- Reset the cadence immediately whenever anything changes (new commit/SHA, check status changes, new review comments, mergeability changes, review decision changes). -- If CI stops being green again (new commit, rerun, or regression): stay on the base polling cadence. -- If any poll shows the PR is merged or otherwise closed: stop polling immediately and report the terminal state. - -## Stop Conditions (Strict) -Stop only when one of the following is true: - -- PR merged or closed (stop as soon as a poll/snapshot confirms this). -- User intervention is required and Codex cannot safely proceed alone. - -Keep polling when: - -- `actions` contains only `idle` but checks are still pending. -- CI is still running/queued. -- Review state is quiet but CI is not terminal. -- CI is green but mergeability is unknown/pending. -- CI is green and mergeable, but the PR is still open and you are waiting for possible new review comments or merge-conflict changes. -- The PR is green but blocked on review approval (`REVIEW_REQUIRED` / similar); continue polling at the base cadence and surface any new review comments without asking for confirmation to keep watching. - -## Output Expectations -Provide concise progress updates while monitoring and a final summary that includes: - -- During long unchanged monitoring periods, avoid emitting a full update on every poll; summarize only status changes plus occasional heartbeat updates. -- Treat push confirmations, intermediate CI snapshots, ready-to-merge snapshots, and review-action updates as progress updates only; do not emit the final summary or end the babysitting session unless a strict stop condition is met. -- A user request to "monitor" is not satisfied by a couple of sample polls; remain in the loop until a strict stop condition or an explicit user interruption. -- A review-fix commit + push is not a completion event; immediately resume live monitoring (`--watch`) in the same turn and continue reporting progress updates. -- When CI first transitions to all green for the current SHA, emit a one-time celebratory progress update (do not repeat it on every green poll). Preferred style: `🚀 CI is all green! 33/33 passed. Still on watch for review approval.` -- Do not send the final summary while a watcher terminal is still running unless the watcher has emitted/confirmed a strict stop condition; otherwise continue with progress updates. - -- Final PR SHA -- CI status summary -- Mergeability / conflict status -- Fixes pushed -- Flaky retry cycles used -- Remaining unresolved failures or review comments - -## References - -- Heuristics and decision tree: `.codex/skills/babysit-pr/references/heuristics.md` -- GitHub CLI/API details used by the watcher: `.codex/skills/babysit-pr/references/github-api-notes.md` diff --git a/reference/openai-codex/.codex/skills/babysit-pr/agents/openai.yaml b/reference/openai-codex/.codex/skills/babysit-pr/agents/openai.yaml deleted file mode 100644 index c6946cf..0000000 --- a/reference/openai-codex/.codex/skills/babysit-pr/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "PR Babysitter" - short_description: "Watch PR review comments, CI, and merge conflicts" - default_prompt: "Babysit the current PR: monitor reviewer comments, CI, and merge-conflict status (prefer the watcher’s --watch mode for live monitoring); surface new review feedback before acting on CI or mergeability work, fix valid issues, push updates, and rerun flaky failures up to 3 times. Do not post replies to human-authored review comments unless the user explicitly confirms the exact response. Do not patch unrelated flaky tests, CI infrastructure, dependency outages, runner issues, or other failures that are not caused by the branch. Keep exactly one watcher session active for the PR (do not leave duplicate --watch terminals running). If you pause monitoring to patch review/CI feedback, restart --watch yourself immediately after the push in the same turn. If a watcher is still running and no strict stop condition has been reached, the task is still in progress: keep consuming watcher output and sending progress updates instead of ending the turn. Do not treat a green + mergeable PR as a terminal stop while it is still open; continue polling autonomously after any push/rerun so newly posted review comments are surfaced until a strict terminal stop condition is reached or the user interrupts." diff --git a/reference/openai-codex/.codex/skills/babysit-pr/references/github-api-notes.md b/reference/openai-codex/.codex/skills/babysit-pr/references/github-api-notes.md deleted file mode 100644 index 8c0a7c8..0000000 --- a/reference/openai-codex/.codex/skills/babysit-pr/references/github-api-notes.md +++ /dev/null @@ -1,82 +0,0 @@ -# GitHub CLI / API Notes For `babysit-pr` - -## Primary commands used - -### PR metadata - -- `gh pr view --json number,url,state,mergedAt,closedAt,headRefName,headRefOid,headRepository,headRepositoryOwner` - -Used to resolve PR number, URL, branch, head SHA, and closed/merged state. - -### PR checks summary - -- `gh pr checks --json name,state,bucket,link,workflow,event,startedAt,completedAt` - -Used to compute pending/failed/passed counts and whether the current CI round is terminal. - -### Workflow runs for head SHA - -- `gh api repos/{owner}/{repo}/actions/runs -X GET -f head_sha= -f per_page=100` - -Used to discover failed workflow runs and rerunnable run IDs. - -### Failed log inspection - -- `gh run view --json jobs,name,workflowName,conclusion,status,url,headSha` -- `gh api repos/{owner}/{repo}/actions/runs/{run_id}/jobs -X GET -f per_page=100` -- `gh api repos/{owner}/{repo}/actions/jobs/{job_id}/logs > /tmp/codex-gh-job-{job_id}-logs.zip` -- `gh run view --log-failed` - -Used by Codex to classify branch-related vs flaky/unrelated failures. Prefer the direct job log endpoint as soon as a job has failed because `gh run view --log-failed` may not produce failed-job logs until the overall workflow run completes. - -### Retry failed jobs only - -- `gh run rerun --failed` - -Reruns only failed jobs (and dependencies) for a workflow run. - -## Review-related endpoints - -- Issue comments on PR: - - `gh api repos/{owner}/{repo}/issues//comments?per_page=100` -- Inline PR review comments: - - `gh api repos/{owner}/{repo}/pulls//comments?per_page=100` -- Review submissions: - - `gh api repos/{owner}/{repo}/pulls//reviews?per_page=100` - -## JSON fields consumed by the watcher - -### `gh pr view` - -- `number` -- `url` -- `state` -- `mergedAt` -- `closedAt` -- `headRefName` -- `headRefOid` - -### `gh pr checks` - -- `bucket` (`pass`, `fail`, `pending`, `skipping`) -- `state` -- `name` -- `workflow` -- `link` - -### Actions runs API (`workflow_runs[]`) - -- `id` -- `name` -- `status` -- `conclusion` -- `html_url` -- `head_sha` - -### Actions run jobs API (`jobs[]`) - -- `id` -- `name` -- `status` -- `conclusion` -- `html_url` diff --git a/reference/openai-codex/.codex/skills/babysit-pr/references/heuristics.md b/reference/openai-codex/.codex/skills/babysit-pr/references/heuristics.md deleted file mode 100644 index ee44c4a..0000000 --- a/reference/openai-codex/.codex/skills/babysit-pr/references/heuristics.md +++ /dev/null @@ -1,66 +0,0 @@ -# CI / Review Heuristics - -## CI classification checklist - -Treat as **branch-related** when logs clearly indicate a regression caused by the PR branch: - -- Compile/typecheck/lint failures in files or modules touched by the branch -- Deterministic unit/integration test failures in changed areas -- Snapshot output changes caused by UI/text changes in the branch -- Static analysis violations introduced by the latest push -- Build script/config changes in the PR causing a deterministic failure - -Treat as **likely flaky or unrelated** when evidence points to transient or external issues: - -- DNS/network/registry timeout errors while fetching dependencies -- Runner image provisioning or startup failures -- GitHub Actions infrastructure/service outages -- Cloud/service rate limits or transient API outages -- Non-deterministic failures in unrelated integration tests with known flake patterns - -Do not patch likely flaky/unrelated failures. Use the retry budget for rerunnable failures, wait for pending jobs, or stop and report the blocker when the failure is persistent or infrastructure-owned. - -If uncertain, inspect failed logs once before choosing rerun. - -## Decision tree (fix vs rerun vs stop) - -1. If PR is merged/closed: stop. -2. If there are failed checks: - - Diagnose first. - - If checks are still pending but an individual job has already failed: fetch that job's logs and diagnose now. - - If branch-related: fix locally, commit, push. - - If likely flaky/unrelated and all checks for the current SHA are terminal: rerun failed jobs. - - If likely flaky/unrelated and not safely rerunnable: stop and report the blocker; do not edit unrelated tests, build scripts, CI configuration, dependency pins, or infrastructure code. - - If checks are still pending and no failed job is available yet: wait. -3. If flaky reruns for the same SHA reach the configured limit (default 3): stop and report persistent failure. -4. Independently, process any new human review comments. - -## Review comment agreement criteria - -Address the comment when: - -- The comment is technically correct. -- The change is actionable in the current branch. -- The requested change does not conflict with the user’s intent or recent guidance. -- The change can be made safely without unrelated refactors. - -Fix valid human review feedback in code when possible, but do not post a GitHub reply to a human-authored comment/thread unless the user explicitly confirms the exact response. - -Do not auto-fix when: - -- The comment is ambiguous and needs clarification. -- The request conflicts with explicit user instructions. -- The proposed change requires product/design decisions the user has not made. -- The codebase is in a dirty/unrelated state that makes safe editing uncertain. -- The comment only needs a written answer or disagreement response; propose the reply to the user instead of posting it automatically. - -## Stop-and-ask conditions - -Stop and ask the user instead of continuing automatically when: - -- The local worktree has unrelated uncommitted changes. -- `gh` auth/permissions fail. -- The PR branch cannot be pushed. -- CI failures persist after the flaky retry budget. -- Reviewer feedback requires a product decision or cross-team coordination. -- A human review comment requires a written GitHub reply instead of a code change. diff --git a/reference/openai-codex/.codex/skills/babysit-pr/scripts/gh_pr_watch.py b/reference/openai-codex/.codex/skills/babysit-pr/scripts/gh_pr_watch.py deleted file mode 100755 index face4e6..0000000 --- a/reference/openai-codex/.codex/skills/babysit-pr/scripts/gh_pr_watch.py +++ /dev/null @@ -1,869 +0,0 @@ -#!/usr/bin/env python3 -"""Watch GitHub PR CI and review activity for Codex PR babysitting workflows.""" - -import argparse -import json -import os -import re -import subprocess -import sys -import tempfile -import time -from pathlib import Path -from urllib.parse import urlparse - -FAILED_RUN_CONCLUSIONS = { - "failure", - "timed_out", - "cancelled", - "action_required", - "startup_failure", - "stale", -} -PENDING_CHECK_STATES = { - "QUEUED", - "IN_PROGRESS", - "PENDING", - "WAITING", - "REQUESTED", -} -REVIEW_BOT_LOGIN_KEYWORDS = { - "codex", -} -TRUSTED_AUTHOR_ASSOCIATIONS = { - "OWNER", - "MEMBER", - "COLLABORATOR", -} -MERGE_BLOCKING_REVIEW_DECISIONS = { - "REVIEW_REQUIRED", - "CHANGES_REQUESTED", -} -MERGE_CONFLICT_OR_BLOCKING_STATES = { - "BLOCKED", - "DIRTY", - "DRAFT", - "UNKNOWN", -} - - -class GhCommandError(RuntimeError): - pass - - -def parse_args(): - parser = argparse.ArgumentParser( - description=( - "Normalize PR/CI/review state for Codex PR babysitting and optionally " - "trigger flaky reruns." - ) - ) - parser.add_argument("--pr", default="auto", help="auto, PR number, or PR URL") - parser.add_argument("--repo", help="Optional OWNER/REPO override") - parser.add_argument("--poll-seconds", type=int, default=30, help="Watch poll interval") - parser.add_argument( - "--max-flaky-retries", - type=int, - default=3, - help="Max rerun cycles per head SHA before stop recommendation", - ) - parser.add_argument("--state-file", help="Path to state JSON file") - parser.add_argument("--once", action="store_true", help="Emit one snapshot and exit") - parser.add_argument("--watch", action="store_true", help="Continuously emit JSONL snapshots") - parser.add_argument( - "--retry-failed-now", - action="store_true", - help="Rerun failed jobs for current failed workflow runs when policy allows", - ) - parser.add_argument( - "--json", - action="store_true", - help="Emit machine-readable output (default behavior for --once and --retry-failed-now)", - ) - args = parser.parse_args() - - if args.poll_seconds <= 0: - parser.error("--poll-seconds must be > 0") - if args.max_flaky_retries < 0: - parser.error("--max-flaky-retries must be >= 0") - if args.watch and args.retry_failed_now: - parser.error("--watch cannot be combined with --retry-failed-now") - if not args.once and not args.watch and not args.retry_failed_now: - args.once = True - return args - - -def _format_gh_error(cmd, err): - stdout = (err.stdout or "").strip() - stderr = (err.stderr or "").strip() - parts = [f"GitHub CLI command failed: {' '.join(cmd)}"] - if stdout: - parts.append(f"stdout: {stdout}") - if stderr: - parts.append(f"stderr: {stderr}") - return "\n".join(parts) - - -def gh_text(args, repo=None): - cmd = ["gh"] - # `gh api` does not accept `-R/--repo` on all gh versions. The watcher's - # API calls use explicit endpoints (e.g. repos/{owner}/{repo}/...), so the - # repo flag is unnecessary there. - if repo and (not args or args[0] != "api"): - cmd.extend(["-R", repo]) - cmd.extend(args) - try: - proc = subprocess.run(cmd, check=True, capture_output=True, text=True) - except FileNotFoundError as err: - raise GhCommandError("`gh` command not found") from err - except subprocess.CalledProcessError as err: - raise GhCommandError(_format_gh_error(cmd, err)) from err - return proc.stdout - - -def gh_json(args, repo=None): - raw = gh_text(args, repo=repo).strip() - if not raw: - return None - try: - return json.loads(raw) - except json.JSONDecodeError as err: - raise GhCommandError(f"Failed to parse JSON from gh output for {' '.join(args)}") from err - - -def parse_pr_spec(pr_spec): - if pr_spec == "auto": - return {"mode": "auto", "value": None} - if re.fullmatch(r"\d+", pr_spec): - return {"mode": "number", "value": pr_spec} - parsed = urlparse(pr_spec) - if parsed.scheme and parsed.netloc and "/pull/" in parsed.path: - return {"mode": "url", "value": pr_spec} - raise ValueError("--pr must be 'auto', a PR number, or a PR URL") - - -def pr_view_fields(): - return ( - "number,url,state,mergedAt,closedAt,headRefName,headRefOid," - "headRepository,headRepositoryOwner,mergeable,mergeStateStatus,reviewDecision" - ) - - -def checks_fields(): - return "name,state,bucket,link,workflow,event,startedAt,completedAt" - - -def resolve_pr(pr_spec, repo_override=None): - parsed = parse_pr_spec(pr_spec) - cmd = ["pr", "view"] - if parsed["value"] is not None: - cmd.append(parsed["value"]) - cmd.extend(["--json", pr_view_fields()]) - data = gh_json(cmd, repo=repo_override) - if not isinstance(data, dict): - raise GhCommandError("Unexpected PR payload from `gh pr view`") - - pr_url = str(data.get("url") or "") - repo = ( - repo_override - or extract_repo_from_pr_url(pr_url) - or extract_repo_from_pr_view(data) - ) - if not repo: - raise GhCommandError("Unable to determine OWNER/REPO for the PR") - - state = str(data.get("state") or "") - merged = bool(data.get("mergedAt")) - closed = bool(data.get("closedAt")) or state.upper() == "CLOSED" - - return { - "number": int(data["number"]), - "url": pr_url, - "repo": repo, - "head_sha": str(data.get("headRefOid") or ""), - "head_branch": str(data.get("headRefName") or ""), - "state": state, - "merged": merged, - "closed": closed, - "mergeable": str(data.get("mergeable") or ""), - "merge_state_status": str(data.get("mergeStateStatus") or ""), - "review_decision": str(data.get("reviewDecision") or ""), - } - - -def extract_repo_from_pr_view(data): - head_repo = data.get("headRepository") - head_owner = data.get("headRepositoryOwner") - owner = None - name = None - if isinstance(head_owner, dict): - owner = head_owner.get("login") or head_owner.get("name") - elif isinstance(head_owner, str): - owner = head_owner - if isinstance(head_repo, dict): - name = head_repo.get("name") - repo_owner = head_repo.get("owner") - if not owner and isinstance(repo_owner, dict): - owner = repo_owner.get("login") or repo_owner.get("name") - elif isinstance(head_repo, str): - name = head_repo - if owner and name: - return f"{owner}/{name}" - return None -def extract_repo_from_pr_url(pr_url): - parsed = urlparse(pr_url) - parts = [p for p in parsed.path.split("/") if p] - if len(parts) >= 4 and parts[2] == "pull": - return f"{parts[0]}/{parts[1]}" - return None - - -def load_state(path): - if path.exists(): - try: - data = json.loads(path.read_text()) - except json.JSONDecodeError as err: - raise RuntimeError(f"State file is not valid JSON: {path}") from err - if not isinstance(data, dict): - raise RuntimeError(f"State file must contain an object: {path}") - return data, False - return { - "pr": {}, - "started_at": None, - "last_seen_head_sha": None, - "retries_by_sha": {}, - "seen_issue_comment_ids": [], - "seen_review_comment_ids": [], - "seen_review_ids": [], - "last_snapshot_at": None, - }, True - - -def save_state(path, state): - path.parent.mkdir(parents=True, exist_ok=True) - payload = json.dumps(state, indent=2, sort_keys=True) + "\n" - fd, tmp_name = tempfile.mkstemp(prefix=f"{path.name}.", suffix=".tmp", dir=path.parent) - tmp_path = Path(tmp_name) - try: - with os.fdopen(fd, "w", encoding="utf-8") as tmp_file: - tmp_file.write(payload) - os.replace(tmp_path, path) - except Exception: - try: - tmp_path.unlink(missing_ok=True) - except OSError: - pass - raise - - -def default_state_file_for(pr): - repo_slug = pr["repo"].replace("/", "-") - return Path(f"/tmp/codex-babysit-pr-{repo_slug}-pr{pr['number']}.json") - - -def get_pr_checks(pr_spec, repo): - parsed = parse_pr_spec(pr_spec) - cmd = ["pr", "checks"] - if parsed["value"] is not None: - cmd.append(parsed["value"]) - cmd.extend(["--json", checks_fields()]) - data = gh_json(cmd, repo=repo) - if data is None: - return [] - if not isinstance(data, list): - raise GhCommandError("Unexpected payload from `gh pr checks`") - return data - - -def is_pending_check(check): - bucket = str(check.get("bucket") or "").lower() - state = str(check.get("state") or "").upper() - return bucket == "pending" or state in PENDING_CHECK_STATES - - -def summarize_checks(checks): - pending_count = 0 - failed_count = 0 - passed_count = 0 - for check in checks: - bucket = str(check.get("bucket") or "").lower() - if is_pending_check(check): - pending_count += 1 - if bucket == "fail": - failed_count += 1 - if bucket == "pass": - passed_count += 1 - return { - "pending_count": pending_count, - "failed_count": failed_count, - "passed_count": passed_count, - "all_terminal": pending_count == 0, - } - - -def get_workflow_runs_for_sha(repo, head_sha): - endpoint = f"repos/{repo}/actions/runs" - data = gh_json( - ["api", endpoint, "-X", "GET", "-f", f"head_sha={head_sha}", "-f", "per_page=100"], - repo=repo, - ) - if not isinstance(data, dict): - raise GhCommandError("Unexpected payload from actions runs API") - runs = data.get("workflow_runs") or [] - if not isinstance(runs, list): - raise GhCommandError("Expected `workflow_runs` to be a list") - return runs - - -def failed_runs_from_workflow_runs(runs, head_sha): - failed_runs = [] - for run in runs: - if not isinstance(run, dict): - continue - if str(run.get("head_sha") or "") != head_sha: - continue - conclusion = str(run.get("conclusion") or "") - if conclusion not in FAILED_RUN_CONCLUSIONS: - continue - failed_runs.append( - { - "run_id": run.get("id"), - "workflow_name": run.get("name") or run.get("display_title") or "", - "status": str(run.get("status") or ""), - "conclusion": conclusion, - "html_url": str(run.get("html_url") or ""), - } - ) - failed_runs.sort(key=lambda item: (str(item.get("workflow_name") or ""), str(item.get("run_id") or ""))) - return failed_runs - - -def get_jobs_for_run(repo, run_id): - endpoint = f"repos/{repo}/actions/runs/{run_id}/jobs" - data = gh_json(["api", endpoint, "-X", "GET", "-f", "per_page=100"], repo=repo) - if not isinstance(data, dict): - raise GhCommandError("Unexpected payload from actions run jobs API") - jobs = data.get("jobs") or [] - if not isinstance(jobs, list): - raise GhCommandError("Expected `jobs` to be a list") - return jobs - - -def failed_jobs_from_workflow_runs(repo, runs, head_sha): - failed_jobs = [] - for run in runs: - if not isinstance(run, dict): - continue - if str(run.get("head_sha") or "") != head_sha: - continue - run_id = run.get("id") - if run_id in (None, ""): - continue - run_status = str(run.get("status") or "") - run_conclusion = str(run.get("conclusion") or "") - if run_status.lower() == "completed" and run_conclusion not in FAILED_RUN_CONCLUSIONS: - continue - jobs = get_jobs_for_run(repo, run_id) - for job in jobs: - if not isinstance(job, dict): - continue - conclusion = str(job.get("conclusion") or "") - if conclusion not in FAILED_RUN_CONCLUSIONS: - continue - job_id = job.get("id") - logs_endpoint = None - if job_id not in (None, ""): - logs_endpoint = f"repos/{repo}/actions/jobs/{job_id}/logs" - failed_jobs.append( - { - "run_id": run_id, - "workflow_name": run.get("name") or run.get("display_title") or "", - "run_status": run_status, - "run_conclusion": run_conclusion, - "job_id": job_id, - "job_name": str(job.get("name") or ""), - "status": str(job.get("status") or ""), - "conclusion": conclusion, - "html_url": str(job.get("html_url") or ""), - "logs_endpoint": logs_endpoint, - } - ) - failed_jobs.sort( - key=lambda item: ( - str(item.get("workflow_name") or ""), - str(item.get("job_name") or ""), - str(item.get("job_id") or ""), - ) - ) - return failed_jobs - - -def get_authenticated_login(): - data = gh_json(["api", "user"]) - if not isinstance(data, dict) or not data.get("login"): - raise GhCommandError("Unable to determine authenticated GitHub login from `gh api user`") - return str(data["login"]) - - -def comment_endpoints(repo, pr_number): - return { - "issue_comment": f"repos/{repo}/issues/{pr_number}/comments", - "review_comment": f"repos/{repo}/pulls/{pr_number}/comments", - "review": f"repos/{repo}/pulls/{pr_number}/reviews", - } - - -def gh_api_list_paginated(endpoint, repo=None, per_page=100): - items = [] - page = 1 - while True: - sep = "&" if "?" in endpoint else "?" - page_endpoint = f"{endpoint}{sep}per_page={per_page}&page={page}" - payload = gh_json(["api", page_endpoint], repo=repo) - if payload is None: - break - if not isinstance(payload, list): - raise GhCommandError(f"Unexpected paginated payload from gh api {endpoint}") - items.extend(payload) - if len(payload) < per_page: - break - page += 1 - return items - - -def normalize_issue_comments(items): - out = [] - for item in items: - if not isinstance(item, dict): - continue - out.append( - { - "kind": "issue_comment", - "id": str(item.get("id") or ""), - "author": extract_login(item.get("user")), - "author_association": str(item.get("author_association") or ""), - "created_at": str(item.get("created_at") or ""), - "body": str(item.get("body") or ""), - "path": None, - "line": None, - "url": str(item.get("html_url") or ""), - } - ) - return out - - -def normalize_review_comments(items): - out = [] - for item in items: - if not isinstance(item, dict): - continue - line = item.get("line") - if line is None: - line = item.get("original_line") - out.append( - { - "kind": "review_comment", - "id": str(item.get("id") or ""), - "author": extract_login(item.get("user")), - "author_association": str(item.get("author_association") or ""), - "created_at": str(item.get("created_at") or ""), - "body": str(item.get("body") or ""), - "path": item.get("path"), - "line": line, - "url": str(item.get("html_url") or ""), - } - ) - return out - - -def normalize_reviews(items): - out = [] - for item in items: - if not isinstance(item, dict): - continue - out.append( - { - "kind": "review", - "id": str(item.get("id") or ""), - "author": extract_login(item.get("user")), - "author_association": str(item.get("author_association") or ""), - "created_at": str(item.get("submitted_at") or item.get("created_at") or ""), - "body": str(item.get("body") or ""), - "path": None, - "line": None, - "url": str(item.get("html_url") or ""), - } - ) - return out - - -def extract_login(user_obj): - if isinstance(user_obj, dict): - return str(user_obj.get("login") or "") - return "" - - -def is_bot_login(login): - return bool(login) and login.endswith("[bot]") - - -def is_actionable_review_bot_login(login): - if not is_bot_login(login): - return False - lower_login = login.lower() - return any(keyword in lower_login for keyword in REVIEW_BOT_LOGIN_KEYWORDS) - - -def is_trusted_human_review_author(item, authenticated_login): - author = str(item.get("author") or "") - if not author: - return False - if authenticated_login and author == authenticated_login: - return True - association = str(item.get("author_association") or "").upper() - return association in TRUSTED_AUTHOR_ASSOCIATIONS - - -def fetch_new_review_items(pr, state, fresh_state, authenticated_login=None): - repo = pr["repo"] - pr_number = pr["number"] - endpoints = comment_endpoints(repo, pr_number) - - issue_payload = gh_api_list_paginated(endpoints["issue_comment"], repo=repo) - review_comment_payload = gh_api_list_paginated(endpoints["review_comment"], repo=repo) - review_payload = gh_api_list_paginated(endpoints["review"], repo=repo) - - issue_items = normalize_issue_comments(issue_payload) - review_comment_items = normalize_review_comments(review_comment_payload) - review_items = normalize_reviews(review_payload) - all_items = issue_items + review_comment_items + review_items - - seen_issue = {str(x) for x in state.get("seen_issue_comment_ids") or []} - seen_review_comment = {str(x) for x in state.get("seen_review_comment_ids") or []} - seen_review = {str(x) for x in state.get("seen_review_ids") or []} - - # On a brand-new state file, surface existing review activity instead of - # silently treating it as seen. This avoids missing already-pending review - # feedback when monitoring starts after comments were posted. - - new_items = [] - for item in all_items: - item_id = item.get("id") - if not item_id: - continue - author = item.get("author") or "" - if not author: - continue - if is_bot_login(author): - if not is_actionable_review_bot_login(author): - continue - elif not is_trusted_human_review_author(item, authenticated_login): - continue - - kind = item["kind"] - if kind == "issue_comment" and item_id in seen_issue: - continue - if kind == "review_comment" and item_id in seen_review_comment: - continue - if kind == "review" and item_id in seen_review: - continue - - new_items.append(item) - if kind == "issue_comment": - seen_issue.add(item_id) - elif kind == "review_comment": - seen_review_comment.add(item_id) - elif kind == "review": - seen_review.add(item_id) - - new_items.sort(key=lambda item: (item.get("created_at") or "", item.get("kind") or "", item.get("id") or "")) - state["seen_issue_comment_ids"] = sorted(seen_issue) - state["seen_review_comment_ids"] = sorted(seen_review_comment) - state["seen_review_ids"] = sorted(seen_review) - return new_items - - -def current_retry_count(state, head_sha): - retries = state.get("retries_by_sha") or {} - value = retries.get(head_sha, 0) - try: - return int(value) - except (TypeError, ValueError): - return 0 - - -def set_retry_count(state, head_sha, count): - retries = state.get("retries_by_sha") - if not isinstance(retries, dict): - retries = {} - retries[head_sha] = int(count) - state["retries_by_sha"] = retries - - -def unique_actions(actions): - out = [] - seen = set() - for action in actions: - if action not in seen: - out.append(action) - seen.add(action) - return out - - -def is_pr_ready_to_merge(pr, checks_summary, new_review_items): - if pr["closed"] or pr["merged"]: - return False - if not checks_summary["all_terminal"]: - return False - if checks_summary["failed_count"] > 0 or checks_summary["pending_count"] > 0: - return False - if new_review_items: - return False - if str(pr.get("mergeable") or "") != "MERGEABLE": - return False - if str(pr.get("merge_state_status") or "") in MERGE_CONFLICT_OR_BLOCKING_STATES: - return False - if str(pr.get("review_decision") or "") in MERGE_BLOCKING_REVIEW_DECISIONS: - return False - return True - - -def recommend_actions(pr, checks_summary, failed_runs, failed_jobs, new_review_items, retries_used, max_retries): - actions = [] - if pr["closed"] or pr["merged"]: - if new_review_items: - actions.append("process_review_comment") - actions.append("stop_pr_closed") - return unique_actions(actions) - - if is_pr_ready_to_merge(pr, checks_summary, new_review_items): - actions.append("ready_to_merge") - return unique_actions(actions) - - if new_review_items: - actions.append("process_review_comment") - - has_failed_pr_checks = checks_summary["failed_count"] > 0 or bool(failed_jobs) - if has_failed_pr_checks: - if checks_summary["all_terminal"] and retries_used >= max_retries: - actions.append("stop_exhausted_retries") - else: - actions.append("diagnose_ci_failure") - if checks_summary["all_terminal"] and failed_runs and retries_used < max_retries: - actions.append("retry_failed_checks") - - if not actions: - actions.append("idle") - return unique_actions(actions) - - -def collect_snapshot(args): - pr = resolve_pr(args.pr, repo_override=args.repo) - state_path = Path(args.state_file) if args.state_file else default_state_file_for(pr) - state, fresh_state = load_state(state_path) - - if not state.get("started_at"): - state["started_at"] = int(time.time()) - - authenticated_login = get_authenticated_login() - new_review_items = fetch_new_review_items( - pr, - state, - fresh_state=fresh_state, - authenticated_login=authenticated_login, - ) - # Surface review feedback before drilling into CI and mergeability details. - # That keeps the babysitter responsive to new comments even when other - # actions are also available. - # `gh pr checks -R ` requires an explicit PR/branch/url argument. - # After resolving `--pr auto`, reuse the concrete PR number. - checks = get_pr_checks(str(pr["number"]), repo=pr["repo"]) - checks_summary = summarize_checks(checks) - workflow_runs = get_workflow_runs_for_sha(pr["repo"], pr["head_sha"]) - failed_runs = failed_runs_from_workflow_runs(workflow_runs, pr["head_sha"]) - failed_jobs = failed_jobs_from_workflow_runs(pr["repo"], workflow_runs, pr["head_sha"]) - - retries_used = current_retry_count(state, pr["head_sha"]) - actions = recommend_actions( - pr, - checks_summary, - failed_runs, - failed_jobs, - new_review_items, - retries_used, - args.max_flaky_retries, - ) - - state["pr"] = {"repo": pr["repo"], "number": pr["number"]} - state["last_seen_head_sha"] = pr["head_sha"] - state["last_snapshot_at"] = int(time.time()) - save_state(state_path, state) - - snapshot = { - "pr": pr, - "checks": checks_summary, - "failed_runs": failed_runs, - "failed_jobs": failed_jobs, - "new_review_items": new_review_items, - "actions": actions, - "retry_state": { - "current_sha_retries_used": retries_used, - "max_flaky_retries": args.max_flaky_retries, - }, - } - return snapshot, state_path - - -def retry_failed_now(args): - snapshot, state_path = collect_snapshot(args) - pr = snapshot["pr"] - checks_summary = snapshot["checks"] - failed_runs = snapshot["failed_runs"] - retries_used = snapshot["retry_state"]["current_sha_retries_used"] - max_retries = snapshot["retry_state"]["max_flaky_retries"] - - result = { - "snapshot": snapshot, - "state_file": str(state_path), - "rerun_attempted": False, - "rerun_count": 0, - "rerun_run_ids": [], - "reason": None, - } - - if pr["closed"] or pr["merged"]: - result["reason"] = "pr_closed" - return result - if checks_summary["failed_count"] <= 0: - result["reason"] = "no_failed_pr_checks" - return result - if not failed_runs: - result["reason"] = "no_failed_runs" - return result - if not checks_summary["all_terminal"]: - result["reason"] = "checks_still_pending" - return result - if retries_used >= max_retries: - result["reason"] = "retry_budget_exhausted" - return result - - for run in failed_runs: - run_id = run.get("run_id") - if run_id in (None, ""): - continue - gh_text(["run", "rerun", str(run_id), "--failed"], repo=pr["repo"]) - result["rerun_run_ids"].append(run_id) - - if result["rerun_run_ids"]: - state, _ = load_state(state_path) - new_count = current_retry_count(state, pr["head_sha"]) + 1 - set_retry_count(state, pr["head_sha"], new_count) - state["last_snapshot_at"] = int(time.time()) - save_state(state_path, state) - result["rerun_attempted"] = True - result["rerun_count"] = len(result["rerun_run_ids"]) - result["reason"] = "rerun_triggered" - else: - result["reason"] = "failed_runs_missing_ids" - - return result - - -def print_json(obj): - sys.stdout.write(json.dumps(obj, sort_keys=True) + "\n") - sys.stdout.flush() - - -def print_event(event, payload): - print_json({"event": event, "payload": payload}) - - -def is_ci_green(snapshot): - checks = snapshot.get("checks") or {} - return ( - bool(checks.get("all_terminal")) - and int(checks.get("failed_count") or 0) == 0 - and int(checks.get("pending_count") or 0) == 0 - ) - - -def snapshot_change_key(snapshot): - pr = snapshot.get("pr") or {} - checks = snapshot.get("checks") or {} - review_items = snapshot.get("new_review_items") or [] - return ( - str(pr.get("head_sha") or ""), - str(pr.get("state") or ""), - str(pr.get("mergeable") or ""), - str(pr.get("merge_state_status") or ""), - str(pr.get("review_decision") or ""), - int(checks.get("passed_count") or 0), - int(checks.get("failed_count") or 0), - int(checks.get("pending_count") or 0), - tuple( - (str(item.get("kind") or ""), str(item.get("id") or "")) - for item in review_items - if isinstance(item, dict) - ), - tuple(snapshot.get("actions") or []), - ) - - -def run_watch(args): - poll_seconds = args.poll_seconds - last_change_key = None - while True: - snapshot, state_path = collect_snapshot(args) - print_event( - "snapshot", - { - "snapshot": snapshot, - "state_file": str(state_path), - "next_poll_seconds": poll_seconds, - }, - ) - actions = set(snapshot.get("actions") or []) - if ( - "stop_pr_closed" in actions - or "stop_exhausted_retries" in actions - ): - print_event("stop", {"actions": snapshot.get("actions"), "pr": snapshot.get("pr")}) - return 0 - - current_change_key = snapshot_change_key(snapshot) - changed = current_change_key != last_change_key - green = is_ci_green(snapshot) - pr = snapshot.get("pr") or {} - pr_open = not bool(pr.get("closed")) and not bool(pr.get("merged")) - - if not green or pr_open: - poll_seconds = args.poll_seconds - elif changed or last_change_key is None: - poll_seconds = args.poll_seconds - - last_change_key = current_change_key - time.sleep(poll_seconds) - - -def main(): - args = parse_args() - try: - if args.retry_failed_now: - print_json(retry_failed_now(args)) - return 0 - if args.watch: - return run_watch(args) - snapshot, state_path = collect_snapshot(args) - snapshot["state_file"] = str(state_path) - print_json(snapshot) - return 0 - except (GhCommandError, RuntimeError, ValueError) as err: - sys.stderr.write(f"gh_pr_watch.py error: {err}\n") - return 1 - except KeyboardInterrupt: - sys.stderr.write("gh_pr_watch.py interrupted\n") - return 130 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/reference/openai-codex/.codex/skills/babysit-pr/scripts/test_gh_pr_watch.py b/reference/openai-codex/.codex/skills/babysit-pr/scripts/test_gh_pr_watch.py deleted file mode 100644 index b636ee4..0000000 --- a/reference/openai-codex/.codex/skills/babysit-pr/scripts/test_gh_pr_watch.py +++ /dev/null @@ -1,217 +0,0 @@ -import argparse -import importlib.util -from pathlib import Path - -import pytest - - -MODULE_PATH = Path(__file__).with_name("gh_pr_watch.py") -MODULE_SPEC = importlib.util.spec_from_file_location("gh_pr_watch", MODULE_PATH) -gh_pr_watch = importlib.util.module_from_spec(MODULE_SPEC) -assert MODULE_SPEC.loader is not None -MODULE_SPEC.loader.exec_module(gh_pr_watch) - - -def sample_pr(): - return { - "number": 123, - "url": "https://github.com/openai/codex/pull/123", - "repo": "openai/codex", - "head_sha": "abc123", - "head_branch": "feature", - "state": "OPEN", - "merged": False, - "closed": False, - "mergeable": "MERGEABLE", - "merge_state_status": "CLEAN", - "review_decision": "", - } - - -def sample_checks(**overrides): - checks = { - "pending_count": 0, - "failed_count": 0, - "passed_count": 12, - "all_terminal": True, - } - checks.update(overrides) - return checks - - -def test_collect_snapshot_fetches_review_items_before_ci(monkeypatch, tmp_path): - call_order = [] - pr = sample_pr() - - monkeypatch.setattr(gh_pr_watch, "resolve_pr", lambda *args, **kwargs: pr) - monkeypatch.setattr(gh_pr_watch, "load_state", lambda path: ({}, True)) - monkeypatch.setattr( - gh_pr_watch, - "get_authenticated_login", - lambda: call_order.append("auth") or "octocat", - ) - monkeypatch.setattr( - gh_pr_watch, - "fetch_new_review_items", - lambda *args, **kwargs: call_order.append("review") or [], - ) - monkeypatch.setattr( - gh_pr_watch, - "get_pr_checks", - lambda *args, **kwargs: call_order.append("checks") or [], - ) - monkeypatch.setattr( - gh_pr_watch, - "summarize_checks", - lambda checks: call_order.append("summarize") or sample_checks(), - ) - monkeypatch.setattr( - gh_pr_watch, - "get_workflow_runs_for_sha", - lambda *args, **kwargs: call_order.append("workflow") or [], - ) - monkeypatch.setattr( - gh_pr_watch, - "failed_runs_from_workflow_runs", - lambda *args, **kwargs: call_order.append("failed_runs") or [], - ) - monkeypatch.setattr( - gh_pr_watch, - "failed_jobs_from_workflow_runs", - lambda *args, **kwargs: call_order.append("failed_jobs") or [], - ) - monkeypatch.setattr( - gh_pr_watch, - "recommend_actions", - lambda *args, **kwargs: call_order.append("recommend") or ["idle"], - ) - monkeypatch.setattr(gh_pr_watch, "save_state", lambda *args, **kwargs: None) - - args = argparse.Namespace( - pr="123", - repo=None, - state_file=str(tmp_path / "watcher-state.json"), - max_flaky_retries=3, - ) - - gh_pr_watch.collect_snapshot(args) - - assert call_order.index("review") < call_order.index("checks") - assert call_order.index("review") < call_order.index("workflow") - - -def test_recommend_actions_prioritizes_review_comments(): - actions = gh_pr_watch.recommend_actions( - sample_pr(), - sample_checks(failed_count=1), - [{"run_id": 99}], - [], - [{"kind": "review_comment", "id": "1"}], - 0, - 3, - ) - - assert actions == [ - "process_review_comment", - "diagnose_ci_failure", - "retry_failed_checks", - ] - - -def test_run_watch_keeps_polling_open_ready_to_merge_pr(monkeypatch): - sleeps = [] - events = [] - snapshot = { - "pr": sample_pr(), - "checks": sample_checks(), - "failed_runs": [], - "failed_jobs": [], - "new_review_items": [], - "actions": ["ready_to_merge"], - "retry_state": { - "current_sha_retries_used": 0, - "max_flaky_retries": 3, - }, - } - - monkeypatch.setattr( - gh_pr_watch, - "collect_snapshot", - lambda args: (snapshot, Path("/tmp/codex-babysit-pr-state.json")), - ) - monkeypatch.setattr( - gh_pr_watch, - "print_event", - lambda event, payload: events.append((event, payload)), - ) - - class StopWatch(Exception): - pass - - def fake_sleep(seconds): - sleeps.append(seconds) - if len(sleeps) >= 2: - raise StopWatch - - monkeypatch.setattr(gh_pr_watch.time, "sleep", fake_sleep) - - with pytest.raises(StopWatch): - gh_pr_watch.run_watch(argparse.Namespace(poll_seconds=30)) - - assert sleeps == [30, 30] - assert [event for event, _ in events] == ["snapshot", "snapshot"] - - -def test_failed_jobs_include_direct_logs_endpoint(monkeypatch): - jobs_by_run = { - 99: [ - { - "id": 555, - "name": "unit tests", - "status": "completed", - "conclusion": "failure", - "html_url": "https://github.com/openai/codex/actions/runs/99/job/555", - }, - { - "id": 556, - "name": "lint", - "status": "completed", - "conclusion": "success", - }, - ] - } - - monkeypatch.setattr( - gh_pr_watch, - "get_jobs_for_run", - lambda repo, run_id: jobs_by_run[run_id], - ) - - failed_jobs = gh_pr_watch.failed_jobs_from_workflow_runs( - "openai/codex", - [ - { - "id": 99, - "name": "CI", - "status": "in_progress", - "conclusion": "", - "head_sha": "abc123", - } - ], - "abc123", - ) - - assert failed_jobs == [ - { - "run_id": 99, - "workflow_name": "CI", - "run_status": "in_progress", - "run_conclusion": "", - "job_id": 555, - "job_name": "unit tests", - "status": "completed", - "conclusion": "failure", - "html_url": "https://github.com/openai/codex/actions/runs/99/job/555", - "logs_endpoint": "repos/openai/codex/actions/jobs/555/logs", - } - ] diff --git a/reference/openai-codex/.codex/skills/code-review-breaking-changes/SKILL.md b/reference/openai-codex/.codex/skills/code-review-breaking-changes/SKILL.md deleted file mode 100644 index d0bddf2..0000000 --- a/reference/openai-codex/.codex/skills/code-review-breaking-changes/SKILL.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -name: code-breaking-changes -description: Breaking changes ---- - -Search for breaking changes in external integration surfaces: -- app-server APIs -- CLI parameters -- configuration loading -- resuming sessions from existing rollouts - -Do not stop after finding one issue; analyze all possible ways breaking changes can happen. diff --git a/reference/openai-codex/.codex/skills/code-review-change-size/SKILL.md b/reference/openai-codex/.codex/skills/code-review-change-size/SKILL.md deleted file mode 100644 index 4e8048d..0000000 --- a/reference/openai-codex/.codex/skills/code-review-change-size/SKILL.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: code-review-change-size -description: Change size guidance (800 lines) ---- - -Unless the change is mechanical the total number of changed lines should not exceed 800 lines. -For complex logic changes the size should be under 500 lines. - -If the change is larger, explain whether it can be split into reviewable stages and identify the smallest coherent stage to land first. -Base the staging suggestion on the actual diff, dependencies, and affected call sites. - diff --git a/reference/openai-codex/.codex/skills/code-review-context/SKILL.md b/reference/openai-codex/.codex/skills/code-review-context/SKILL.md deleted file mode 100644 index 7faf3d7..0000000 --- a/reference/openai-codex/.codex/skills/code-review-context/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: code-review-context -description: Model visible context ---- - -Codex maintains a context (history of messages) that is sent to the model in inference requests. - -1. No history rewrite - the context must be built up incrementally. -2. Avoid frequent changes to context that cause cache misses. -3. No unbounded items - everything injected in the model context must have a bounded size and a hard cap. -4. No items larger than 10K tokens. -5. Highlight new individual items that can cross >1k tokens as P0. These need an additional manual review. -6. All injected fragments must be defined as structs in `core/context` and implement ContextualUserFragment trait \ No newline at end of file diff --git a/reference/openai-codex/.codex/skills/code-review-testing/SKILL.md b/reference/openai-codex/.codex/skills/code-review-testing/SKILL.md deleted file mode 100644 index c8d99e1..0000000 --- a/reference/openai-codex/.codex/skills/code-review-testing/SKILL.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: code-review-testing -description: Test authoring guidance ---- - -For agent changes prefer integration tests over unit tests. Integration tests are under `core/suite` and use `test_codex` to set up a test instance of codex. - -Features that change the agent logic MUST add an integration test: -- Provide a list of major logic changes and user-facing behaviors that need to be tested. - -If unit tests are needed, put them in a dedicated test file (*_tests.rs). -Avoid test-only functions in the main implementation. - -Check whether there are existing helpers to make tests more streamlined and readable. diff --git a/reference/openai-codex/.codex/skills/code-review/SKILL.md b/reference/openai-codex/.codex/skills/code-review/SKILL.md deleted file mode 100644 index eec0787..0000000 --- a/reference/openai-codex/.codex/skills/code-review/SKILL.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: code-review -description: Run a final code review on a pull request ---- - -Use subagents to review code using all code-review-* skills in this repository other than this orchestrator. One subagent per skill. Pass full skill path to subagents. Use xhigh reasoning. - -You must return every single issue from every subagent. You can return an unlimited number of findings. -Use raw Markdown to report findings. -Number findings for ease of reference. -Each finding must include a specific file path and line number. - -If the GitHub user running the review is the owner of the pull request add a `code-reviewed` label. -Do not leave GitHub comments unless explicitly asked. diff --git a/reference/openai-codex/.codex/skills/codex-bug/SKILL.md b/reference/openai-codex/.codex/skills/codex-bug/SKILL.md deleted file mode 100644 index c7a688e..0000000 --- a/reference/openai-codex/.codex/skills/codex-bug/SKILL.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -name: codex-bug -description: Diagnose GitHub bug reports in openai/codex. Use when given a GitHub issue URL from openai/codex and asked to decide next steps such as verifying against the repo, requesting more info, or explaining why it is not a bug; follow any additional user-provided instructions. ---- - -# Codex Bug - -## Overview - -Diagnose a Codex GitHub bug report and decide the next action: verify against sources, request more info, or explain why it is not a bug. - -## Workflow - -1. Confirm the input - -- Require a GitHub issue URL that points to `github.com/openai/codex/issues/…`. -- If the URL is missing or not in the right repo, ask the user for the correct link. - -2. Network access - -- Always access the issue over the network immediately, even if you think access is blocked or unavailable. -- Prefer the GitHub API over HTML pages because the HTML is noisy: - - Issue: `https://api.github.com/repos/openai/codex/issues/` - - Comments: `https://api.github.com/repos/openai/codex/issues//comments` -- If the environment requires explicit approval, request it on demand via the tool and continue without additional user prompting. -- Only if the network attempt fails after requesting approval, explain what you can do offline (e.g., draft a response template) and ask how to proceed. - -3. Read the issue - -- Use the GitHub API responses (issue + comments) as the source of truth rather than scraping the HTML issue page. -- Extract: title, body, repro steps, expected vs actual, environment, logs, and any attachments. -- Note whether the report already includes logs or session details. -- If the report includes a thread ID, mention it in the summary and use it to look up the logs and session details if you have access to them. - -4. Summarize the bug before investigating - -- Before inspecting code, docs, or logs in depth, write a short summary of the report in your own words. -- Include the reported behavior, expected behavior, repro steps, environment, and what evidence is already attached or missing. - -5. Decide the course of action - -- **Verify with sources** when the report is specific and likely reproducible. Inspect relevant Codex files (or mention the files to inspect if access is unavailable). -- **Request more information** when the report is vague, missing repro steps, or lacks logs/environment. -- **Explain not a bug** when the report contradicts current behavior or documented constraints (cite the evidence from the issue and any local sources you checked). - -6. Respond - -- Provide a concise report of your findings and next steps. diff --git a/reference/openai-codex/.codex/skills/codex-issue-digest/SKILL.md b/reference/openai-codex/.codex/skills/codex-issue-digest/SKILL.md deleted file mode 100644 index bae4bf9..0000000 --- a/reference/openai-codex/.codex/skills/codex-issue-digest/SKILL.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -name: codex-issue-digest -description: Run a GitHub issue digest for openai/codex by feature-area labels, all areas, and configurable time windows. Use when asked to summarize recent Codex bug reports or enhancement requests, especially for owner-specific labels such as tui, exec, app, or similar areas. ---- - -# Codex Issue Digest - -## Objective - -Produce a headline-first, insight-oriented digest of `openai/codex` issues for the requested feature-area labels over the previous 24 hours by default. Honor a different duration when the user asks for one, for example "past week" or "48 hours". Default to a summary-only response; include details only when requested. - -Include only issues that currently have `bug` or `enhancement` plus at least one requested owner label. If the user asks for all areas or all labels, collect `bug`/`enhancement` issues across all labels. - -## Inputs - -- Feature-area labels, for example `tui exec` -- `all areas` / `all labels` to scan all current feature labels -- Optional repo override, default `openai/codex` -- Optional time window, default previous 24 hours; examples: `48h`, `7d`, `1w`, `past week` - -## Workflow - -1. Run the collector from a current Codex repo checkout: - -```bash -python3 .codex/skills/codex-issue-digest/scripts/collect_issue_digest.py --labels tui exec --window-hours 24 -``` - -Use `--window "past week"` or `--window-hours 168` when the user asks for a non-default duration. Use `--all-labels` when the user says all areas or all labels. - -2. Use the JSON as the source of truth. It includes new issues, new issue comments, new reactions/upvotes, current labels, current reaction counts, model-ready `summary_inputs`, and detailed `digest_rows`. -3. Choose the output mode from the user's request: - - Default mode: start the report with `## Summary` and do not emit `## Details`. - - Details-upfront mode: if the user asks for details, a table, a full digest, "include details", or similar, start with `## Summary`, then include `## Details`. - - Follow-up details mode: if the user asks for more detail after a summary-only digest, produce `## Details` from the existing collector JSON when it is still available; otherwise rerun the collector. -4. In `## Summary`, write a headline-first executive summary: - - The first nonblank line under `## Summary` must be a single-line headline or judgment, not a bullet. It should be useful even if the reader stops there. - - On quiet days, prefer exactly: `No major issues reported by users.` Use this when there are no elevated rows, no newly repeated theme, and nothing that needs owner action. - - When users are surfacing notable issues, make the headline name the count or theme, for example `Two issues are being surfaced by users:`. - - Immediately under an active headline, list only the issues or themes driving attention, ordered by importance. Start each line with the row's `attention_marker` when present, then a concise owner-readable description and inline issue refs. - - Treat `🔥🔥` as headline-worthy and `🔥` as elevated. Do not add fire emoji yourself; only copy the row's `attention_marker`. - - Keep any extra summary detail after the headline to 1-3 terse lines, only when it adds a decision-relevant caveat, repeated theme, or owner action. - - Do not include routine counts, broad stats, or low-signal table summaries in `## Summary` unless they change the headline. Put metadata and optional counts in `## Details` or the footer. - - In default mode, end the report with a concise prompt such as `Want details? I can expand this into the issue table.` Keep this separate from the summary headline so the headline stays clean. - - Cluster and name themes yourself from `summary_inputs`; the collector intentionally does not hard-code issue categories. - - Use a cluster only when the issues genuinely share the same product problem. If several issues merely share a broad platform or label, describe them individually. - - Do not omit a repeated theme just because its individual issues fall below the details table cutoff. Several similar reports should be called out as a repeated customer concern. - - For single-issue rows, summarize the concern directly instead of calling it a cluster. - - Use inline numbered issue links from each relevant row's `ref_markdown`. - - Example quiet summary: - -```markdown -## Summary -No major issues reported by users. - -Source: collector v5, git `abc123def456`, window `2026-04-27T00:00:00Z` to `2026-04-28T00:00:00Z`. -Want details? I can expand this into the issue table. -``` - - - Example active summary: - -```markdown -## Summary -Two issues are being surfaced by users: -🔥🔥 Terminal launch hangs on startup [1](https://github.com/openai/codex/issues/123) -🔥 Resume switches model providers unexpectedly [2](https://github.com/openai/codex/issues/456) - -Source: collector v5, git `abc123def456`, window `2026-04-27T00:00:00Z` to `2026-04-28T00:00:00Z`. -Want details? I can expand this into the issue table. -``` -5. In `## Details`, when details are requested, include a compact table only when useful: - - Prefer rows from `digest_rows`; include a `Refs` column using each row's `ref_markdown`. - - Keep the table short; omit low-signal rows when the summary already covers them. - - Use compact columns such as marker, area, type, description, interactions, and refs. - - The `Description` cell should be a short owner-readable phrase. Use row `description`, title, body excerpts, and recent comments, but do not mechanically copy the raw GitHub issue title when it contains incidental details. - - A clear quiet/no-concern sentence when there is no meaningful signal. -6. Use the JSON `attention_marker` exactly. It is empty for normal rows, `🔥` for elevated rows, and `🔥🔥` for very high-attention rows. The actual cutoffs are in `attention_thresholds`. -7. Use inline numbered references where a row or bullet points to issues, for example `Compaction bugs [1](https://github.com/openai/codex/issues/123), [2](https://github.com/openai/codex/issues/456)`. Do not add a separate footnotes section. -8. Label `interactions` as `Interactions`; it counts unique human GitHub users who created a new issue, added a new comment, or reacted during the requested window. Multiple posts/reactions from the same user on the same issue count once. -9. Mention the collector `script_version`, repo checkout `git_head`, and time window in one compact source line. In default mode, put this before the details prompt so the final line still asks whether the user wants details. In details-upfront mode, it can be the footer. - -## Reaction Handling - -The collector uses GitHub reactions endpoints, which include `created_at`, to count reactions created during the digest window for hydrated issues. It reports both in-window reaction counts and current reaction totals. Treat current reaction totals as standing engagement, and treat `new_reactions` / `new_upvotes` as windowed activity. - -By default, the collector fetches issue comments with `since=` and caps the number of comment pages per issue. This keeps very long historical threads from dominating a digest run and focuses the report on recent posts. Use `--fetch-all-comments` only when exhaustive comment history is more important than runtime. - -GitHub issue search is still seeded by issue `updated_at`, so a purely reaction-only issue may be missed if reactions do not bump `updated_at`. Covering every reaction-only case would require either a persisted snapshot store or a broader scan of labeled issues. - -## Attention Markers - -The collector scales attention markers by the requested time window. The baseline is 5 unique human users for `🔥` and 10 unique human users for `🔥🔥` over 24 hours; longer or shorter windows scale those cutoffs linearly and round up. For example, a one-week report uses 35 and 70 interactions. Unique human users are users who authored a new issue, authored a new comment, or reacted during the window, including upvotes. Multiple actions from the same user on the same issue count once. Bot posts and bot reactions are excluded. In prose, explain this as high user interaction rather than naming the emoji. - -## Freshness - -The automation should run from a repo checkout that contains this skill. For shared daily use, prefer one of these patterns: - -- Run the automation in a checkout that is refreshed before the automation starts, for example with `git pull --ff-only`. -- If the automation cannot safely mutate the checkout, have it report the current `git_head` from the collector output so readers know which skill/script version produced the digest. - -## Sample Owner Prompt - -```text -Use $codex-issue-digest to run the Codex issue digest for labels tui and exec over the previous 24 hours. -``` - -```text -Use $codex-issue-digest to run the Codex issue digest for all areas over the past week. -``` - -## Validation - -Dry run the collector against recent issues: - -```bash -python3 .codex/skills/codex-issue-digest/scripts/collect_issue_digest.py --labels tui exec --window-hours 24 -``` - -```bash -python3 .codex/skills/codex-issue-digest/scripts/collect_issue_digest.py --all-labels --window "past week" --limit-issues 10 -``` - -Run the focused script tests: - -```bash -pytest .codex/skills/codex-issue-digest/scripts/test_collect_issue_digest.py -``` diff --git a/reference/openai-codex/.codex/skills/codex-issue-digest/agents/openai.yaml b/reference/openai-codex/.codex/skills/codex-issue-digest/agents/openai.yaml deleted file mode 100644 index 706ce5e..0000000 --- a/reference/openai-codex/.codex/skills/codex-issue-digest/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Codex Issue Digest" - short_description: "Summarize Codex issues by labels or all areas" - default_prompt: "Use $codex-issue-digest to run the Codex issue digest for labels tui and exec over the previous 24 hours." diff --git a/reference/openai-codex/.codex/skills/codex-issue-digest/scripts/collect_issue_digest.py b/reference/openai-codex/.codex/skills/codex-issue-digest/scripts/collect_issue_digest.py deleted file mode 100755 index 80b54d6..0000000 --- a/reference/openai-codex/.codex/skills/codex-issue-digest/scripts/collect_issue_digest.py +++ /dev/null @@ -1,1013 +0,0 @@ -#!/usr/bin/env python3 -"""Collect recent openai/codex issue activity for owner-focused digests.""" - -import argparse -import json -import math -import re -import subprocess -import sys -from datetime import datetime, timedelta, timezone -from pathlib import Path -from urllib.parse import quote - -SCRIPT_VERSION = 5 -QUALIFYING_KIND_LABELS = ("bug", "enhancement") -REACTION_KEYS = ("+1", "-1", "laugh", "hooray", "confused", "heart", "rocket", "eyes") -BASE_ATTENTION_WINDOW_HOURS = 24.0 -ONE_ATTENTION_INTERACTION_THRESHOLD = 5 -TWO_ATTENTION_INTERACTION_THRESHOLD = 10 -ALL_LABEL_PHRASES = {"all", "all areas", "all labels", "all-areas", "all-labels", "*"} - - -class GhCommandError(RuntimeError): - pass - - -def parse_args(): - parser = argparse.ArgumentParser( - description="Collect recent GitHub issue activity for a Codex owner digest." - ) - parser.add_argument( - "--repo", default="openai/codex", help="OWNER/REPO, default openai/codex" - ) - parser.add_argument( - "--labels", - nargs="+", - default=[], - help="Feature-area labels owned by the digest recipient, for example: tui exec", - ) - parser.add_argument( - "--all-labels", - action="store_true", - help="Collect bug/enhancement issues across all feature-area labels", - ) - parser.add_argument( - "--window", - help='Lookback duration such as "24h", "7d", "1w", or "past week"', - ) - parser.add_argument( - "--window-hours", type=float, default=24.0, help="Lookback window" - ) - parser.add_argument( - "--since", help="UTC ISO timestamp override for the window start" - ) - parser.add_argument("--until", help="UTC ISO timestamp override for the window end") - parser.add_argument( - "--limit-issues", - type=int, - default=200, - help="Maximum candidate issues to hydrate after search", - ) - parser.add_argument( - "--body-chars", type=int, default=1200, help="Issue body excerpt length" - ) - parser.add_argument( - "--comment-chars", type=int, default=900, help="Comment excerpt length" - ) - parser.add_argument( - "--max-comment-pages", - type=int, - default=3, - help=( - "Maximum pages of issue comments to hydrate per issue after applying the " - "window filter. Use 0 with --fetch-all-comments for no page cap." - ), - ) - parser.add_argument( - "--fetch-all-comments", - action="store_true", - help="Hydrate complete issue comment histories instead of only window-updated comments.", - ) - return parser.parse_args() - - -def parse_timestamp(value, arg_name): - if value is None: - return None - normalized = value.strip() - if not normalized: - return None - if normalized.endswith("Z"): - normalized = f"{normalized[:-1]}+00:00" - try: - parsed = datetime.fromisoformat(normalized) - except ValueError as err: - raise ValueError(f"{arg_name} must be an ISO timestamp") from err - if parsed.tzinfo is None: - parsed = parsed.replace(tzinfo=timezone.utc) - return parsed.astimezone(timezone.utc) - - -def format_timestamp(value): - return ( - value.astimezone(timezone.utc) - .replace(microsecond=0) - .isoformat() - .replace("+00:00", "Z") - ) - - -def resolve_window(args): - until = parse_timestamp(args.until, "--until") or datetime.now(timezone.utc) - since = parse_timestamp(args.since, "--since") - if since is None: - hours = parse_duration_hours(getattr(args, "window", None)) - if hours is None: - hours = getattr(args, "window_hours", 24.0) - if hours <= 0: - raise ValueError("window duration must be > 0") - since = until - timedelta(hours=hours) - if since >= until: - raise ValueError("--since must be before --until") - return since, until - - -def parse_duration_hours(value): - if value is None: - return None - text = value.strip().casefold().replace("_", " ") - if not text: - return None - text = re.sub(r"^(past|last)\s+", "", text) - aliases = { - "day": 24.0, - "24h": 24.0, - "week": 168.0, - "7d": 168.0, - } - if text in aliases: - return aliases[text] - match = re.fullmatch(r"(\d+(?:\.\d+)?)\s*(h|hr|hrs|hour|hours)", text) - if match: - return float(match.group(1)) - match = re.fullmatch(r"(\d+(?:\.\d+)?)\s*(d|day|days)", text) - if match: - return float(match.group(1)) * 24.0 - match = re.fullmatch(r"(\d+(?:\.\d+)?)\s*(w|week|weeks)", text) - if match: - return float(match.group(1)) * 168.0 - raise ValueError(f"Unsupported duration: {value}") - - -def normalize_requested_labels(labels, all_labels=False): - out = [] - seen = set() - for raw in labels: - for piece in raw.split(","): - label = piece.strip() - if not label: - continue - key = label.casefold() - if key not in seen: - out.append(label) - seen.add(key) - phrase = " ".join(label.casefold() for label in out) - if all_labels or phrase in ALL_LABEL_PHRASES: - return [], True - if not out: - raise ValueError( - "At least one feature-area label is required, or use --all-labels" - ) - return out, False - - -def quote_label(label): - if re.fullmatch(r"[A-Za-z0-9_.:-]+", label): - return f"label:{label}" - escaped = label.replace('"', '\\"') - return f'label:"{escaped}"' - - -def build_search_queries( - repo, owner_labels, since, kind_labels=QUALIFYING_KIND_LABELS, all_labels=False -): - since_date = since.date().isoformat() - queries = [] - if all_labels: - for kind_label in kind_labels: - queries.append( - " ".join( - [ - f"repo:{repo}", - "is:issue", - f"updated:>={since_date}", - quote_label(kind_label), - ] - ) - ) - return queries - for owner_label in owner_labels: - for kind_label in kind_labels: - queries.append( - " ".join( - [ - f"repo:{repo}", - "is:issue", - f"updated:>={since_date}", - quote_label(owner_label), - quote_label(kind_label), - ] - ) - ) - return queries - - -def _format_gh_error(cmd, err): - stdout = (err.stdout or "").strip() - stderr = (err.stderr or "").strip() - parts = [f"GitHub CLI command failed: {' '.join(cmd)}"] - if stdout: - parts.append(f"stdout: {stdout}") - if stderr: - parts.append(f"stderr: {stderr}") - return "\n".join(parts) - - -def gh_json(args): - cmd = ["gh", *args] - try: - proc = subprocess.run(cmd, check=True, capture_output=True, text=True) - except FileNotFoundError as err: - raise GhCommandError("`gh` command not found") from err - except subprocess.CalledProcessError as err: - raise GhCommandError(_format_gh_error(cmd, err)) from err - raw = proc.stdout.strip() - if not raw: - return None - try: - return json.loads(raw) - except json.JSONDecodeError as err: - raise GhCommandError( - f"Failed to parse JSON from gh output for {' '.join(args)}" - ) from err - - -def gh_text(args): - cmd = ["gh", *args] - try: - proc = subprocess.run(cmd, check=True, capture_output=True, text=True) - except (FileNotFoundError, subprocess.CalledProcessError): - return "" - return proc.stdout.strip() - - -def git_head(): - try: - proc = subprocess.run( - ["git", "rev-parse", "--short=12", "HEAD"], - check=True, - capture_output=True, - text=True, - ) - except (FileNotFoundError, subprocess.CalledProcessError): - return None - return proc.stdout.strip() or None - - -def skill_relative_path(): - try: - return str(Path(__file__).resolve().relative_to(Path.cwd().resolve())) - except ValueError: - return str(Path(__file__).resolve()) - - -def gh_api_list_paginated(endpoint, per_page=100, max_pages=None, with_metadata=False): - items = [] - page = 1 - truncated = False - while True: - sep = "&" if "?" in endpoint else "?" - page_endpoint = f"{endpoint}{sep}per_page={per_page}&page={page}" - payload = gh_json(["api", page_endpoint]) - if payload is None: - break - if not isinstance(payload, list): - raise GhCommandError(f"Unexpected paginated payload from gh api {endpoint}") - items.extend(payload) - if len(payload) < per_page: - break - if max_pages is not None and page >= max_pages: - truncated = True - break - page += 1 - if with_metadata: - return { - "items": items, - "truncated": truncated, - "pages": page, - "max_pages": max_pages, - } - return items - - -def search_issue_numbers(queries, limit): - numbers = {} - for query in queries: - page = 1 - seen_for_query = 0 - while True: - payload = gh_json( - [ - "api", - "search/issues", - "-X", - "GET", - "-f", - f"q={query}", - "-f", - "sort=updated", - "-f", - "order=desc", - "-f", - "per_page=100", - "-f", - f"page={page}", - ] - ) - if not isinstance(payload, dict): - raise GhCommandError("Unexpected payload from GitHub issue search") - items = payload.get("items") or [] - if not isinstance(items, list): - raise GhCommandError("Expected search `items` to be a list") - for item in items: - if not isinstance(item, dict): - continue - number = item.get("number") - if isinstance(number, int): - numbers[number] = str(item.get("updated_at") or "") - seen_for_query += 1 - if len(items) < 100 or seen_for_query >= limit: - break - page += 1 - ordered = sorted( - numbers, key=lambda number: (numbers[number], number), reverse=True - ) - return ordered[:limit] - - -def fetch_issue(repo, number): - payload = gh_json(["api", f"repos/{repo}/issues/{number}"]) - if not isinstance(payload, dict): - raise GhCommandError(f"Unexpected issue payload for #{number}") - return payload - - -def fetch_comments(repo, number, since=None, max_pages=None): - endpoint = f"repos/{repo}/issues/{number}/comments" - if since is not None: - endpoint = f"{endpoint}?since={quote(format_timestamp(since), safe='')}" - return gh_api_list_paginated( - endpoint, - max_pages=max_pages, - with_metadata=True, - ) - - -def fetch_reactions_for_item(endpoint, item): - if reaction_summary(item)["total"] <= 0: - return [] - return gh_api_list_paginated(endpoint) - - -def fetch_comment_reactions(repo, comments): - reactions_by_comment_id = {} - for comment in comments: - comment_id = comment.get("id") - if comment_id in (None, ""): - continue - endpoint = f"repos/{repo}/issues/comments/{comment_id}/reactions" - reactions_by_comment_id[comment_id] = fetch_reactions_for_item( - endpoint, comment - ) - return reactions_by_comment_id - - -def extract_login(user_obj): - if isinstance(user_obj, dict): - return str(user_obj.get("login") or "") - return "" - - -def is_bot_login(login): - return bool(login) and login.lower().endswith("[bot]") - - -def human_login_key(user_obj): - login = extract_login(user_obj) - if not login or is_bot_login(login): - return "" - return login.casefold() - - -def is_human_user(user_obj): - return bool(human_login_key(user_obj)) - - -def label_names(issue): - labels = [] - for label in issue.get("labels") or []: - if isinstance(label, dict) and label.get("name"): - labels.append(str(label["name"])) - return sorted(labels, key=str.casefold) - - -def matching_labels(labels, requested): - labels_by_key = {label.casefold(): label for label in labels} - return [label for label in requested if label.casefold() in labels_by_key] - - -def area_labels(labels): - kind_keys = {label.casefold() for label in QUALIFYING_KIND_LABELS} - return [label for label in labels if label.casefold() not in kind_keys] - - -def attention_thresholds_for_window(window_hours): - if window_hours <= 0: - raise ValueError("window_hours must be > 0") - window_hours = round(window_hours, 6) - scale = window_hours / BASE_ATTENTION_WINDOW_HOURS - elevated = max(1, math.ceil(ONE_ATTENTION_INTERACTION_THRESHOLD * scale)) - very_high = max( - elevated + 1, math.ceil(TWO_ATTENTION_INTERACTION_THRESHOLD * scale) - ) - return { - "base_window_hours": BASE_ATTENTION_WINDOW_HOURS, - "window_hours": round(window_hours, 3), - "scale": round(scale, 3), - "elevated": elevated, - "very_high": very_high, - } - - -def attention_level_for(user_interactions, attention_thresholds=None): - thresholds = attention_thresholds or attention_thresholds_for_window( - BASE_ATTENTION_WINDOW_HOURS - ) - if user_interactions >= thresholds["very_high"]: - return 2 - if user_interactions >= thresholds["elevated"]: - return 1 - return 0 - - -def attention_marker_for(user_interactions, attention_thresholds=None): - return "🔥" * attention_level_for(user_interactions, attention_thresholds) - - -def reaction_summary(item): - reactions = item.get("reactions") - if not isinstance(reactions, dict): - return {"total": 0, "counts": {}} - counts = {} - for key in REACTION_KEYS: - value = reactions.get(key, 0) - if isinstance(value, int) and value: - counts[key] = value - total = reactions.get("total_count") - if not isinstance(total, int): - total = sum(counts.values()) - return {"total": total, "counts": counts} - - -def reaction_event_summary(reactions, since, until): - counts = {} - total = 0 - users = set() - for reaction in reactions or []: - if not isinstance(reaction, dict): - continue - if not is_in_window(str(reaction.get("created_at") or ""), since, until): - continue - user_key = human_login_key(reaction.get("user")) - if not user_key: - continue - content = str(reaction.get("content") or "") - if not content: - continue - counts[content] = counts.get(content, 0) + 1 - total += 1 - users.add(user_key) - return { - "total": total, - "counts": counts, - "upvotes": counts.get("+1", 0), - "users": sorted(users, key=str.casefold), - } - - -def compact_text(value, limit): - text = re.sub(r"\s+", " ", str(value or "")).strip() - if limit <= 0: - return "" - if len(text) <= limit: - return text - return f"{text[: max(limit - 1, 0)].rstrip()}..." - - -def clean_title_for_description(title): - cleaned = re.sub(r"\s+", " ", str(title or "")).strip() - cleaned = re.sub( - r"^(codex(?: desktop| app|\.app| cli)?|desktop|windows codex app)\s*[:,-]\s*", - "", - cleaned, - flags=re.IGNORECASE, - ) - cleaned = re.sub(r"^on windows,\s*", "Windows: ", cleaned, flags=re.IGNORECASE) - cleaned = cleaned.strip(" -:;") - return compact_text(cleaned, 80) or "Issue needs owner review" - - -def issue_description(issue): - return clean_title_for_description(issue.get("title")) - - -def is_in_window(timestamp, since, until): - parsed = parse_timestamp(timestamp, "timestamp") - if parsed is None: - return False - return since <= parsed < until - - -def summarize_comment( - comment, comment_chars, reaction_events=None, since=None, until=None -): - reactions = reaction_summary(comment) - new_reactions = ( - reaction_event_summary(reaction_events, since, until) - if since is not None and until is not None - else {"total": 0, "counts": {}, "upvotes": 0} - ) - human_user_interaction = is_human_user(comment.get("user")) - return { - "id": comment.get("id"), - "author": extract_login(comment.get("user")), - "author_association": str(comment.get("author_association") or ""), - "created_at": str(comment.get("created_at") or ""), - "updated_at": str(comment.get("updated_at") or ""), - "url": str(comment.get("html_url") or ""), - "human_user_interaction": human_user_interaction, - "reactions": reactions["counts"], - "reaction_total": reactions["total"], - "new_reactions": new_reactions["total"], - "new_upvotes": new_reactions["upvotes"], - "new_reaction_counts": new_reactions["counts"], - "body_excerpt": compact_text(comment.get("body"), comment_chars), - } - - -def summarize_issue( - issue, - comments, - requested_labels, - since, - until, - body_chars, - comment_chars, - issue_reaction_events=None, - comment_reactions_by_id=None, - all_labels=False, - comments_hydration=None, - attention_thresholds=None, -): - labels = label_names(issue) - labels_by_key = {label.casefold() for label in labels} - kind_labels = [ - label for label in QUALIFYING_KIND_LABELS if label.casefold() in labels_by_key - ] - if all_labels: - owner_labels = area_labels(labels) or ["unlabeled"] - else: - owner_labels = matching_labels(labels, requested_labels) - if not kind_labels or not owner_labels: - return None - - updated_at = str(issue.get("updated_at") or "") - if not is_in_window(updated_at, since, until): - return None - - new_issue = is_in_window(str(issue.get("created_at") or ""), since, until) - comment_reactions_by_id = comment_reactions_by_id or {} - new_comments = [ - summarize_comment( - comment, - comment_chars, - reaction_events=comment_reactions_by_id.get(comment.get("id")), - since=since, - until=until, - ) - for comment in comments - if is_in_window(str(comment.get("created_at") or ""), since, until) - ] - new_comments.sort(key=lambda item: (item["created_at"], str(item["id"]))) - - issue_reactions = reaction_summary(issue) - issue_reaction_events_summary = reaction_event_summary( - issue_reaction_events, since, until - ) - comment_reaction_events_summary = reaction_event_summary( - [ - reaction - for reactions in comment_reactions_by_id.values() - for reaction in reactions - ], - since, - until, - ) - new_reactions = ( - issue_reaction_events_summary["total"] - + comment_reaction_events_summary["total"] - ) - new_upvotes = ( - issue_reaction_events_summary["upvotes"] - + comment_reaction_events_summary["upvotes"] - ) - all_comment_reaction_total = sum( - reaction_summary(comment)["total"] for comment in comments - ) - new_comment_reaction_total = sum( - comment["reaction_total"] for comment in new_comments - ) - new_issue_user_key = human_login_key(issue.get("user")) if new_issue else "" - new_issue_user_interaction = bool(new_issue_user_key) - new_comment_user_interactions = sum( - 1 for comment in new_comments if comment["human_user_interaction"] - ) - interaction_user_keys = set(issue_reaction_events_summary["users"]) - interaction_user_keys.update(comment_reaction_events_summary["users"]) - if new_issue_user_key: - interaction_user_keys.add(new_issue_user_key) - interaction_user_keys.update( - comment["author"].casefold() - for comment in new_comments - if comment["human_user_interaction"] - ) - user_interactions = len(interaction_user_keys) - attention_level = attention_level_for(user_interactions, attention_thresholds) - attention_marker = attention_marker_for(user_interactions, attention_thresholds) - updated_without_visible_new_post = ( - not new_issue and not new_comments and new_reactions == 0 - ) - - engagement_score = ( - len(new_comments) * 3 - + new_reactions - + issue_reactions["total"] - + new_comment_reaction_total - + min(int(issue.get("comments") or len(comments) or 0), 10) - ) - - return { - "number": issue.get("number"), - "title": str(issue.get("title") or ""), - "description": issue_description(issue), - "url": str(issue.get("html_url") or ""), - "state": str(issue.get("state") or ""), - "author": extract_login(issue.get("user")), - "author_association": str(issue.get("author_association") or ""), - "created_at": str(issue.get("created_at") or ""), - "updated_at": updated_at, - "labels": labels, - "kind_labels": kind_labels, - "owner_labels": owner_labels, - "comments_total": int(issue.get("comments") or len(comments) or 0), - "comments_hydration": comments_hydration - or { - "fetched": len(comments), - "since": None, - "truncated": False, - "max_pages": None, - }, - "issue_reactions": issue_reactions["counts"], - "issue_reaction_total": issue_reactions["total"], - "comment_reaction_total": all_comment_reaction_total, - "new_comment_reaction_total": new_comment_reaction_total, - "new_issue_reactions": issue_reaction_events_summary["total"], - "new_issue_upvotes": issue_reaction_events_summary["upvotes"], - "new_comment_reactions": comment_reaction_events_summary["total"], - "new_comment_upvotes": comment_reaction_events_summary["upvotes"], - "new_reactions": new_reactions, - "new_upvotes": new_upvotes, - "user_interactions": user_interactions, - "attention": attention_level > 0, - "attention_level": attention_level, - "attention_marker": attention_marker, - "engagement_score": engagement_score, - "activity": { - "new_issue": new_issue, - "new_comments": len(new_comments), - "new_human_comments": new_comment_user_interactions, - "new_reactions": new_reactions, - "new_upvotes": new_upvotes, - "updated_without_visible_new_post": updated_without_visible_new_post, - }, - "body_excerpt": compact_text(issue.get("body"), body_chars), - "new_comments": new_comments, - } - - -def count_by_label(issues, labels): - out = {} - for label in labels: - matching = [issue for issue in issues if label in issue["owner_labels"]] - out[label] = { - "issues": len(matching), - "new_issues": sum( - 1 for issue in matching if issue["activity"]["new_issue"] - ), - "new_comments": sum( - issue["activity"]["new_comments"] for issue in matching - ), - } - return out - - -def count_by_kind(issues): - out = {} - for kind in QUALIFYING_KIND_LABELS: - matching = [issue for issue in issues if kind in issue["kind_labels"]] - out[kind] = { - "issues": len(matching), - "new_issues": sum( - 1 for issue in matching if issue["activity"]["new_issue"] - ), - "new_comments": sum( - issue["activity"]["new_comments"] for issue in matching - ), - } - return out - - -def hot_items(issues, limit=8): - ranked = sorted( - issues, - key=lambda issue: ( - issue["attention"], - issue["attention_level"], - issue["user_interactions"], - issue["engagement_score"], - issue["activity"]["new_comments"], - issue["issue_reaction_total"] + issue["comment_reaction_total"], - issue["updated_at"], - ), - reverse=True, - ) - return [ - { - "number": issue["number"], - "title": issue["title"], - "url": issue["url"], - "owner_labels": issue["owner_labels"], - "kind_labels": issue["kind_labels"], - "attention": issue["attention"], - "attention_level": issue["attention_level"], - "attention_marker": issue["attention_marker"], - "user_interactions": issue["user_interactions"], - "new_reactions": issue["new_reactions"], - "new_upvotes": issue["new_upvotes"], - "engagement_score": issue["engagement_score"], - "new_comments": issue["activity"]["new_comments"], - "reaction_total": issue["issue_reaction_total"] - + issue["comment_reaction_total"], - } - for issue in ranked[:limit] - if issue["engagement_score"] > 0 - ] - - -def ranked_digest_issues(issues): - return sorted( - issues, - key=lambda issue: ( - issue["attention"], - issue["attention_level"], - issue["user_interactions"], - issue["engagement_score"], - issue["activity"]["new_comments"], - issue["updated_at"], - ), - reverse=True, - ) - - -def digest_rows(issues, limit=10, ref_map=None): - ranked = ranked_digest_issues(issues) - if ref_map is None: - ref_map = {issue["number"]: ref for ref, issue in enumerate(ranked, start=1)} - rows = [] - for issue in ranked[:limit]: - ref = ref_map[issue["number"]] - reaction_total = issue["issue_reaction_total"] + issue["comment_reaction_total"] - rows.append( - { - "ref": ref, - "ref_markdown": f"[{ref}]({issue['url']})", - "marker": issue["attention_marker"], - "attention_marker": issue["attention_marker"], - "number": issue["number"], - "description": issue["description"], - "title": issue["title"], - "url": issue["url"], - "area": ", ".join(issue["owner_labels"]), - "kind": ", ".join(issue["kind_labels"]), - "state": issue["state"], - "interactions": issue["user_interactions"], - "user_interactions": issue["user_interactions"], - "new_reactions": issue["new_reactions"], - "new_upvotes": issue["new_upvotes"], - "current_reactions": reaction_total, - } - ) - return rows - - -def issue_ref_markdown(issue, ref_map): - ref = ref_map[issue["number"]] - return f"[{ref}]({issue['url']})" - - -def summary_inputs(issues, limit=80, ref_map=None): - ranked = ranked_digest_issues(issues) - if ref_map is None: - ref_map = {issue["number"]: ref for ref, issue in enumerate(ranked, start=1)} - rows = [] - for issue in ranked[:limit]: - rows.append( - { - "ref": ref_map[issue["number"]], - "ref_markdown": issue_ref_markdown(issue, ref_map), - "number": issue["number"], - "title": issue["title"], - "description": issue["description"], - "url": issue["url"], - "labels": issue["labels"], - "owner_labels": issue["owner_labels"], - "kind_labels": issue["kind_labels"], - "state": issue.get("state", ""), - "attention_marker": issue.get("attention_marker", ""), - "interactions": issue["user_interactions"], - "new_comments": issue["activity"].get("new_comments", 0), - "new_reactions": issue.get("new_reactions", 0), - "new_upvotes": issue.get("new_upvotes", 0), - "current_reactions": issue.get("issue_reaction_total", 0) - + issue.get("comment_reaction_total", 0), - } - ) - return rows - - -def collect_digest(args): - since, until = resolve_window(args) - window_hours = (until - since).total_seconds() / 3600 - attention_thresholds = attention_thresholds_for_window(window_hours) - requested_labels, all_labels = normalize_requested_labels( - args.labels, all_labels=args.all_labels - ) - queries = build_search_queries( - args.repo, requested_labels, since, all_labels=all_labels - ) - numbers = search_issue_numbers(queries, args.limit_issues) - gh_version_output = gh_text(["--version"]) - - issues = [] - max_comment_pages = None if args.max_comment_pages <= 0 else args.max_comment_pages - for number in numbers: - issue = fetch_issue(args.repo, number) - comments_since = None if args.fetch_all_comments else since - comments_payload = fetch_comments( - args.repo, - number, - since=comments_since, - max_pages=max_comment_pages, - ) - comments = comments_payload["items"] - issue_reaction_events = fetch_reactions_for_item( - f"repos/{args.repo}/issues/{number}/reactions", issue - ) - comment_reactions_by_id = fetch_comment_reactions(args.repo, comments) - comments_hydration = { - "fetched": len(comments), - "total": int(issue.get("comments") or len(comments) or 0), - "since": format_timestamp(comments_since) if comments_since else None, - "truncated": comments_payload["truncated"], - "max_pages": comments_payload["max_pages"], - "fetch_all_comments": args.fetch_all_comments, - } - summary = summarize_issue( - issue, - comments, - requested_labels, - since, - until, - args.body_chars, - args.comment_chars, - issue_reaction_events=issue_reaction_events, - comment_reactions_by_id=comment_reactions_by_id, - all_labels=all_labels, - comments_hydration=comments_hydration, - attention_thresholds=attention_thresholds, - ) - if summary is not None: - issues.append(summary) - - issues.sort( - key=lambda issue: (issue["updated_at"], int(issue["number"] or 0)), reverse=True - ) - totals = { - "candidate_issues": len(numbers), - "included_issues": len(issues), - "new_issues": sum(1 for issue in issues if issue["activity"]["new_issue"]), - "issues_with_new_comments": sum( - 1 for issue in issues if issue["activity"]["new_comments"] > 0 - ), - "new_comments": sum(issue["activity"]["new_comments"] for issue in issues), - "comments_fetched": sum( - issue["comments_hydration"]["fetched"] for issue in issues - ), - "issues_with_truncated_comment_hydration": sum( - 1 for issue in issues if issue["comments_hydration"]["truncated"] - ), - "updated_without_visible_new_post": sum( - 1 - for issue in issues - if issue["activity"]["updated_without_visible_new_post"] - ), - "issue_reactions_current_total": sum( - issue["issue_reaction_total"] for issue in issues - ), - "comment_reactions_current_total": sum( - issue["comment_reaction_total"] for issue in issues - ), - "new_reactions": sum(issue["new_reactions"] for issue in issues), - "new_upvotes": sum(issue["new_upvotes"] for issue in issues), - "user_interactions": sum(issue["user_interactions"] for issue in issues), - } - ranked = ranked_digest_issues(issues) - ref_map = {issue["number"]: ref for ref, issue in enumerate(ranked, start=1)} - filter_label = "all" if all_labels else requested_labels - - return { - "generated_at": format_timestamp(datetime.now(timezone.utc)), - "source": { - "repo": args.repo, - "skill": "codex-issue-digest", - "collector": skill_relative_path(), - "script_version": SCRIPT_VERSION, - "git_head": git_head(), - "gh_version": gh_version_output.splitlines()[0] - if gh_version_output - else None, - }, - "window": { - "since": format_timestamp(since), - "until": format_timestamp(until), - "hours": round(window_hours, 3), - }, - "attention_thresholds": attention_thresholds, - "filters": { - "owner_labels": filter_label, - "all_labels": all_labels, - "kind_labels": list(QUALIFYING_KIND_LABELS), - }, - "collection_notes": [ - "Issues are selected when they currently have bug or enhancement plus at least one requested owner label and were updated during the window.", - "By default, issue comments are fetched with since=window_start and a max page cap to avoid long historical threads; use --fetch-all-comments when exhaustive comment history is needed.", - "New issue comments are filtered by comment creation time within the window from the fetched comment set.", - "Reaction events are counted by GitHub reaction created_at timestamps for hydrated issues and fetched comments.", - "Current reaction totals are standing engagement signals; new_reactions and new_upvotes are windowed activity.", - "user_interactions counts unique human users per issue across new issues, new comments, and new reactions; repeated actions by the same user count once.", - "The collector does not assign semantic clusters; use summary_inputs as model-ready evidence for report-time clustering.", - "Pure reaction-only issues may be missed if GitHub issue search does not surface them via updated_at.", - "Issues updated during the window without a new issue body or new comment are retained because label/status edits can still be useful owner signals.", - ], - "totals": totals, - "by_owner_label": count_by_label( - issues, - sorted( - {area for issue in issues for area in issue["owner_labels"]}, - key=str.casefold, - ) - if all_labels - else requested_labels, - ), - "by_kind_label": count_by_kind(issues), - "hot_items": hot_items(issues), - "summary_inputs": summary_inputs(issues, ref_map=ref_map), - "digest_rows": digest_rows(issues, ref_map=ref_map), - "issues": issues, - } - - -def main(): - args = parse_args() - try: - digest = collect_digest(args) - except (GhCommandError, RuntimeError, ValueError) as err: - sys.stderr.write(f"collect_issue_digest.py error: {err}\n") - return 1 - sys.stdout.write(json.dumps(digest, indent=2, sort_keys=True) + "\n") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/reference/openai-codex/.codex/skills/codex-issue-digest/scripts/test_collect_issue_digest.py b/reference/openai-codex/.codex/skills/codex-issue-digest/scripts/test_collect_issue_digest.py deleted file mode 100644 index f1ba54e..0000000 --- a/reference/openai-codex/.codex/skills/codex-issue-digest/scripts/test_collect_issue_digest.py +++ /dev/null @@ -1,749 +0,0 @@ -import importlib.util -from datetime import timezone -from pathlib import Path - - -MODULE_PATH = Path(__file__).with_name("collect_issue_digest.py") -MODULE_SPEC = importlib.util.spec_from_file_location( - "collect_issue_digest", MODULE_PATH -) -collect_issue_digest = importlib.util.module_from_spec(MODULE_SPEC) -assert MODULE_SPEC.loader is not None -MODULE_SPEC.loader.exec_module(collect_issue_digest) - - -def test_build_search_queries_uses_each_owner_and_kind_label(): - since = collect_issue_digest.parse_timestamp("2026-04-25T12:34:56Z", "--since") - - queries = collect_issue_digest.build_search_queries( - "openai/codex", ["tui", "exec"], since - ) - - assert queries == [ - "repo:openai/codex is:issue updated:>=2026-04-25 label:tui label:bug", - "repo:openai/codex is:issue updated:>=2026-04-25 label:tui label:enhancement", - "repo:openai/codex is:issue updated:>=2026-04-25 label:exec label:bug", - "repo:openai/codex is:issue updated:>=2026-04-25 label:exec label:enhancement", - ] - - -def test_build_search_queries_can_scan_all_labels(): - since = collect_issue_digest.parse_timestamp("2026-04-25T12:34:56Z", "--since") - - queries = collect_issue_digest.build_search_queries( - "openai/codex", [], since, all_labels=True - ) - - assert queries == [ - "repo:openai/codex is:issue updated:>=2026-04-25 label:bug", - "repo:openai/codex is:issue updated:>=2026-04-25 label:enhancement", - ] - - -def test_normalize_requested_labels_accepts_all_area_phrases(): - assert collect_issue_digest.normalize_requested_labels(["all", "areas"]) == ( - [], - True, - ) - assert collect_issue_digest.normalize_requested_labels(["all-labels"]) == ( - [], - True, - ) - - -def test_search_issue_numbers_requests_updated_sort(monkeypatch): - calls = [] - - def fake_gh_json(args): - calls.append(args) - return { - "items": [ - {"number": 1, "updated_at": "2026-04-25T00:00:00Z"}, - ] - } - - monkeypatch.setattr(collect_issue_digest, "gh_json", fake_gh_json) - - assert collect_issue_digest.search_issue_numbers(["query"], limit=10) == [1] - assert "-f" in calls[0] - assert "sort=updated" in calls[0] - assert "order=desc" in calls[0] - - -def test_search_issue_numbers_applies_limit_per_query(monkeypatch): - calls = [] - - def fake_gh_json(args): - calls.append(args) - query = next( - value.removeprefix("q=") for value in args if value.startswith("q=") - ) - page = int( - next( - value.removeprefix("page=") - for value in args - if value.startswith("page=") - ) - ) - base = 10_000 if query == "first" else 20_000 - offset = (page - 1) * 100 - return { - "items": [ - { - "number": base + offset + idx, - "updated_at": f"2026-04-25T00:{idx:02d}:00Z", - } - for idx in range(100) - ] - } - - monkeypatch.setattr(collect_issue_digest, "gh_json", fake_gh_json) - - collect_issue_digest.search_issue_numbers(["first", "second"], limit=150) - - queried_pages = [ - ( - next( - value.removeprefix("q=") for value in args if value.startswith("q=") - ), - next( - value.removeprefix("page=") - for value in args - if value.startswith("page=") - ), - ) - for args in calls - ] - assert queried_pages == [ - ("first", "1"), - ("first", "2"), - ("second", "1"), - ("second", "2"), - ] - - -def test_summarize_issue_keeps_new_comments_and_reaction_signals(): - since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") - until = collect_issue_digest.parse_timestamp("2026-04-26T00:00:00Z", "--until") - issue = { - "number": 123, - "title": "TUI does not redraw", - "html_url": "https://github.com/openai/codex/issues/123", - "state": "open", - "created_at": "2026-04-24T20:00:00Z", - "updated_at": "2026-04-25T10:00:00Z", - "user": {"login": "alice"}, - "author_association": "NONE", - "comments": 2, - "body": "The terminal freezes after resize.", - "labels": [{"name": "bug"}, {"name": "tui"}], - "reactions": {"total_count": 3, "+1": 2, "rocket": 1}, - } - comments = [ - { - "id": 1, - "created_at": "2026-04-25T11:00:00Z", - "updated_at": "2026-04-25T11:00:00Z", - "html_url": "https://github.com/openai/codex/issues/123#issuecomment-1", - "user": {"login": "bob"}, - "author_association": "MEMBER", - "body": "I can reproduce this on main.", - "reactions": {"total_count": 4, "heart": 1, "+1": 3}, - }, - { - "id": 2, - "created_at": "2026-04-24T11:00:00Z", - "updated_at": "2026-04-24T11:00:00Z", - "html_url": "https://github.com/openai/codex/issues/123#issuecomment-2", - "user": {"login": "carol"}, - "author_association": "NONE", - "body": "Older comment.", - "reactions": {"total_count": 1, "eyes": 1}, - }, - ] - - summary = collect_issue_digest.summarize_issue( - issue, - comments, - ["tui", "exec"], - since, - until, - body_chars=200, - comment_chars=200, - ) - - assert summary == { - "number": 123, - "title": "TUI does not redraw", - "description": "TUI does not redraw", - "url": "https://github.com/openai/codex/issues/123", - "state": "open", - "author": "alice", - "author_association": "NONE", - "created_at": "2026-04-24T20:00:00Z", - "updated_at": "2026-04-25T10:00:00Z", - "labels": ["bug", "tui"], - "kind_labels": ["bug"], - "owner_labels": ["tui"], - "comments_total": 2, - "comments_hydration": { - "fetched": 2, - "since": None, - "truncated": False, - "max_pages": None, - }, - "issue_reactions": {"+1": 2, "rocket": 1}, - "issue_reaction_total": 3, - "comment_reaction_total": 5, - "new_comment_reaction_total": 4, - "new_issue_reactions": 0, - "new_issue_upvotes": 0, - "new_comment_reactions": 0, - "new_comment_upvotes": 0, - "new_reactions": 0, - "new_upvotes": 0, - "user_interactions": 1, - "attention": False, - "attention_level": 0, - "attention_marker": "", - "engagement_score": 12, - "activity": { - "new_issue": False, - "new_comments": 1, - "new_human_comments": 1, - "new_reactions": 0, - "new_upvotes": 0, - "updated_without_visible_new_post": False, - }, - "body_excerpt": "The terminal freezes after resize.", - "new_comments": [ - { - "id": 1, - "author": "bob", - "author_association": "MEMBER", - "created_at": "2026-04-25T11:00:00Z", - "updated_at": "2026-04-25T11:00:00Z", - "url": "https://github.com/openai/codex/issues/123#issuecomment-1", - "human_user_interaction": True, - "reactions": {"+1": 3, "heart": 1}, - "reaction_total": 4, - "new_reactions": 0, - "new_upvotes": 0, - "new_reaction_counts": {}, - "body_excerpt": "I can reproduce this on main.", - } - ], - } - - -def test_summarize_issue_filters_non_owner_or_non_kind_labels(): - since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") - until = collect_issue_digest.parse_timestamp("2026-04-26T00:00:00Z", "--until") - base_issue = { - "number": 1, - "title": "Question", - "created_at": "2026-04-25T01:00:00Z", - "updated_at": "2026-04-25T01:00:00Z", - "labels": [{"name": "question"}, {"name": "tui"}], - } - - assert ( - collect_issue_digest.summarize_issue( - base_issue, - [], - ["tui"], - since, - until, - body_chars=100, - comment_chars=100, - ) - is None - ) - - issue_without_owner = dict(base_issue) - issue_without_owner["labels"] = [{"name": "bug"}, {"name": "app"}] - - assert ( - collect_issue_digest.summarize_issue( - issue_without_owner, - [], - ["tui"], - since, - until, - body_chars=100, - comment_chars=100, - ) - is None - ) - - -def test_resolve_window_defaults_to_previous_hours(): - class Args: - since = None - until = "2026-04-26T12:00:00Z" - window_hours = 24 - - since, until = collect_issue_digest.resolve_window(Args()) - - assert since.isoformat() == "2026-04-25T12:00:00+00:00" - assert until.tzinfo == timezone.utc - - -def test_parse_duration_hours_accepts_common_phrases(): - assert collect_issue_digest.parse_duration_hours("past week") == 168 - assert collect_issue_digest.parse_duration_hours("48h") == 48 - assert collect_issue_digest.parse_duration_hours("2 days") == 48 - assert collect_issue_digest.parse_duration_hours("1w") == 168 - - -def test_attention_thresholds_scale_by_window_length(): - one_day = collect_issue_digest.attention_thresholds_for_window(24) - assert one_day["elevated"] == 5 - assert one_day["very_high"] == 10 - - half_day = collect_issue_digest.attention_thresholds_for_window(12) - assert half_day["elevated"] == 3 - assert half_day["very_high"] == 5 - - week = collect_issue_digest.attention_thresholds_for_window(168) - assert week["elevated"] == 35 - assert week["very_high"] == 70 - assert collect_issue_digest.attention_marker_for(34, week) == "" - assert collect_issue_digest.attention_marker_for(35, week) == "🔥" - assert collect_issue_digest.attention_marker_for(70, week) == "🔥🔥" - - -def test_fetch_comments_uses_since_filter_and_page_cap(monkeypatch): - calls = [] - - def fake_gh_json(args): - calls.append(args) - return [{"id": idx} for idx in range(100)] - - monkeypatch.setattr(collect_issue_digest, "gh_json", fake_gh_json) - since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") - - payload = collect_issue_digest.fetch_comments( - "openai/codex", 123, since=since, max_pages=1 - ) - - assert len(payload["items"]) == 100 - assert payload["truncated"] is True - assert payload["max_pages"] == 1 - assert calls == [ - [ - "api", - "repos/openai/codex/issues/123/comments?since=2026-04-25T00%3A00%3A00Z&per_page=100&page=1", - ] - ] - - -def test_issue_description_prefers_title_over_body_noise(): - issue = { - "title": "Codex.app GUI: MCP child processes not reaped after task completion", - "body": "A later crash mention should not override the title-level symptom.", - "labels": [{"name": "app"}, {"name": "bug"}], - } - - description = collect_issue_digest.issue_description(issue) - assert "MCP child processes" in description - assert "crash" not in description.casefold() - - -def test_attention_markers_count_human_user_interactions(): - since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") - until = collect_issue_digest.parse_timestamp("2026-04-26T00:00:00Z", "--until") - issue = { - "number": 456, - "title": "Agent context is exploding", - "html_url": "https://github.com/openai/codex/issues/456", - "state": "open", - "created_at": "2026-04-25T01:00:00Z", - "updated_at": "2026-04-25T12:00:00Z", - "user": {"login": "alice"}, - "labels": [{"name": "bug"}, {"name": "agent"}], - } - comments = [ - { - "id": idx, - "created_at": "2026-04-25T02:00:00Z", - "updated_at": "2026-04-25T02:00:00Z", - "user": {"login": f"user-{idx}"}, - "body": "same here", - } - for idx in range(4) - ] - comments.append( - { - "id": 99, - "created_at": "2026-04-25T02:00:00Z", - "updated_at": "2026-04-25T02:00:00Z", - "user": {"login": "github-actions[bot]"}, - "body": "duplicate bot note", - } - ) - - summary = collect_issue_digest.summarize_issue( - issue, - comments, - ["agent"], - since, - until, - body_chars=100, - comment_chars=100, - ) - - assert summary["user_interactions"] == 5 - assert summary["activity"]["new_human_comments"] == 4 - assert summary["attention"] is True - assert summary["attention_level"] == 1 - assert summary["attention_marker"] == "🔥" - - issue["created_at"] = "2026-04-24T01:00:00Z" - comments.extend( - { - "id": idx, - "created_at": "2026-04-25T03:00:00Z", - "updated_at": "2026-04-25T03:00:00Z", - "user": {"login": f"extra-user-{idx}"}, - "body": "also seeing this", - } - for idx in range(100, 106) - ) - - summary = collect_issue_digest.summarize_issue( - issue, - comments, - ["agent"], - since, - until, - body_chars=100, - comment_chars=100, - ) - - assert summary["user_interactions"] == 10 - assert summary["attention_level"] == 2 - assert summary["attention_marker"] == "🔥🔥" - - -def test_reactions_count_toward_attention_markers(): - since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") - until = collect_issue_digest.parse_timestamp("2026-04-26T00:00:00Z", "--until") - issue = { - "number": 789, - "title": "Support 1M token context", - "html_url": "https://github.com/openai/codex/issues/789", - "state": "open", - "created_at": "2026-04-24T01:00:00Z", - "updated_at": "2026-04-25T12:00:00Z", - "user": {"login": "alice"}, - "labels": [{"name": "enhancement"}, {"name": "context"}], - "reactions": {"total_count": 20, "+1": 20}, - } - comments = [ - { - "id": 1, - "created_at": "2026-04-25T02:00:00Z", - "updated_at": "2026-04-25T02:00:00Z", - "user": {"login": "commenter"}, - "body": "please", - "reactions": {"total_count": 2, "+1": 2}, - } - ] - issue_reactions = [ - { - "content": "+1", - "created_at": "2026-04-25T03:00:00Z", - "user": {"login": f"reactor-{idx}"}, - } - for idx in range(18) - ] - comment_reactions_by_id = { - 1: [ - { - "content": "heart", - "created_at": "2026-04-25T04:00:00Z", - "user": {"login": "human-reactor"}, - }, - { - "content": "+1", - "created_at": "2026-04-25T04:00:00Z", - "user": {"login": "github-actions[bot]"}, - }, - ] - } - - summary = collect_issue_digest.summarize_issue( - issue, - comments, - ["context"], - since, - until, - body_chars=100, - comment_chars=100, - issue_reaction_events=issue_reactions, - comment_reactions_by_id=comment_reactions_by_id, - ) - - assert summary["new_reactions"] == 19 - assert summary["new_upvotes"] == 18 - assert summary["user_interactions"] == 20 - assert summary["attention_level"] == 2 - assert summary["attention_marker"] == "🔥🔥" - assert summary["new_comments"][0]["new_reactions"] == 1 - assert summary["new_comments"][0]["new_upvotes"] == 0 - - -def test_user_interactions_are_deduped_by_human_login(): - since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") - until = collect_issue_digest.parse_timestamp("2026-04-26T00:00:00Z", "--until") - - def comment(comment_id, login): - return { - "id": comment_id, - "created_at": f"2026-04-25T0{comment_id + 1}:00:00Z", - "updated_at": f"2026-04-25T0{comment_id + 1}:00:00Z", - "user": {"login": login}, - "body": "same issue", - } - - def reaction(content, login, created_at="2026-04-25T10:00:00Z"): - return { - "content": content, - "created_at": created_at, - "user": {"login": login}, - } - - issue = { - "number": 790, - "title": "Repeated pings should not boost attention", - "html_url": "https://github.com/openai/codex/issues/790", - "state": "open", - "created_at": "2026-04-25T01:00:00Z", - "updated_at": "2026-04-25T12:00:00Z", - "user": {"login": "Alice"}, - "labels": [{"name": "bug"}, {"name": "tui"}], - } - comments = [comment(1, "alice"), comment(2, "ALICE"), comment(3, "bob")] - comments.append(comment(4, "github-actions[bot]")) - issue_reactions = [ - reaction("+1", "alice"), - reaction("rocket", "Alice"), - reaction("+1", "bob"), - reaction("+1", "github-actions[bot]"), - reaction("+1", "carol", created_at="2026-04-24T23:00:00Z"), - ] - comment_reactions_by_id = { - 1: [reaction("heart", "alice")], - 2: [reaction("+1", "bob")], - 3: [reaction("eyes", "carol")], - } - - summary = collect_issue_digest.summarize_issue( - issue, - comments, - ["tui"], - since, - until, - body_chars=100, - comment_chars=100, - issue_reaction_events=issue_reactions, - comment_reactions_by_id=comment_reactions_by_id, - ) - - assert summary["activity"]["new_human_comments"] == 3 - assert summary["new_reactions"] == 6 - assert summary["user_interactions"] == 3 - assert summary["attention"] is False - assert summary["attention_marker"] == "" - - -def test_digest_rows_are_table_ready_with_concise_descriptions(): - rows = collect_issue_digest.digest_rows( - [ - { - "number": 1, - "title": "Quiet bug", - "description": "Quiet bug", - "url": "https://github.com/openai/codex/issues/1", - "owner_labels": ["context"], - "kind_labels": ["bug"], - "state": "open", - "attention": False, - "attention_level": 0, - "attention_marker": "", - "user_interactions": 1, - "new_reactions": 0, - "new_upvotes": 0, - "engagement_score": 3, - "issue_reaction_total": 0, - "comment_reaction_total": 0, - "updated_at": "2026-04-25T01:00:00Z", - "activity": { - "new_issue": True, - "new_comments": 0, - "new_reactions": 0, - "updated_without_visible_new_post": False, - }, - }, - { - "number": 2, - "title": "Busy bug", - "description": "High-volume bug report", - "url": "https://github.com/openai/codex/issues/2", - "owner_labels": ["agent"], - "kind_labels": ["bug"], - "state": "open", - "attention": True, - "attention_level": 1, - "attention_marker": "🔥", - "user_interactions": 17, - "new_reactions": 3, - "new_upvotes": 2, - "engagement_score": 20, - "issue_reaction_total": 5, - "comment_reaction_total": 2, - "updated_at": "2026-04-25T02:00:00Z", - "activity": { - "new_issue": False, - "new_comments": 16, - "new_reactions": 3, - "updated_without_visible_new_post": False, - }, - }, - ] - ) - - assert rows[0] == { - "ref": 1, - "ref_markdown": "[1](https://github.com/openai/codex/issues/2)", - "marker": "🔥", - "attention_marker": "🔥", - "number": 2, - "description": "High-volume bug report", - "title": "Busy bug", - "url": "https://github.com/openai/codex/issues/2", - "area": "agent", - "kind": "bug", - "state": "open", - "interactions": 17, - "user_interactions": 17, - "new_reactions": 3, - "new_upvotes": 2, - "current_reactions": 7, - } - - -def test_summary_inputs_are_model_ready_without_preclustering(): - issues = [ - { - "number": 20, - "title": "Windows app Browser Use external navigation fails", - "description": "Browser Use navigation or app-server failure", - "url": "https://github.com/openai/codex/issues/20", - "labels": ["app", "bug"], - "owner_labels": ["app"], - "kind_labels": ["bug"], - "attention": False, - "attention_level": 0, - "attention_marker": "", - "user_interactions": 3, - "new_reactions": 1, - "engagement_score": 8, - "updated_at": "2026-04-25T04:00:00Z", - "activity": {"new_comments": 2}, - }, - { - "number": 21, - "title": "On Windows, cmake output waits until timeout", - "description": "Windows command timeout/capture problem", - "url": "https://github.com/openai/codex/issues/21", - "labels": ["app", "bug"], - "owner_labels": ["app"], - "kind_labels": ["bug"], - "attention": False, - "attention_level": 0, - "attention_marker": "", - "user_interactions": 3, - "new_reactions": 0, - "engagement_score": 7, - "updated_at": "2026-04-25T03:00:00Z", - "activity": {"new_comments": 3}, - }, - { - "number": 22, - "title": "Windows computer use tool fails to click buttons", - "description": "Computer-use workflow failure", - "url": "https://github.com/openai/codex/issues/22", - "labels": ["app", "bug"], - "owner_labels": ["app"], - "kind_labels": ["bug"], - "attention": False, - "attention_level": 0, - "attention_marker": "", - "user_interactions": 3, - "new_reactions": 0, - "engagement_score": 6, - "updated_at": "2026-04-25T02:00:00Z", - "activity": {"new_comments": 3}, - }, - ] - - rows = collect_issue_digest.summary_inputs(issues, ref_map={20: 1, 21: 2, 22: 3}) - - assert rows == [ - { - "ref": 1, - "ref_markdown": "[1](https://github.com/openai/codex/issues/20)", - "number": 20, - "title": "Windows app Browser Use external navigation fails", - "description": "Browser Use navigation or app-server failure", - "url": "https://github.com/openai/codex/issues/20", - "labels": ["app", "bug"], - "owner_labels": ["app"], - "kind_labels": ["bug"], - "state": "", - "attention_marker": "", - "interactions": 3, - "new_comments": 2, - "new_reactions": 1, - "new_upvotes": 0, - "current_reactions": 0, - }, - { - "ref": 2, - "ref_markdown": "[2](https://github.com/openai/codex/issues/21)", - "number": 21, - "title": "On Windows, cmake output waits until timeout", - "description": "Windows command timeout/capture problem", - "url": "https://github.com/openai/codex/issues/21", - "labels": ["app", "bug"], - "owner_labels": ["app"], - "kind_labels": ["bug"], - "state": "", - "attention_marker": "", - "interactions": 3, - "new_comments": 3, - "new_reactions": 0, - "new_upvotes": 0, - "current_reactions": 0, - }, - { - "ref": 3, - "ref_markdown": "[3](https://github.com/openai/codex/issues/22)", - "number": 22, - "title": "Windows computer use tool fails to click buttons", - "description": "Computer-use workflow failure", - "url": "https://github.com/openai/codex/issues/22", - "labels": ["app", "bug"], - "owner_labels": ["app"], - "kind_labels": ["bug"], - "state": "", - "attention_marker": "", - "interactions": 3, - "new_comments": 3, - "new_reactions": 0, - "new_upvotes": 0, - "current_reactions": 0, - }, - ] diff --git a/reference/openai-codex/.codex/skills/codex-pr-body/SKILL.md b/reference/openai-codex/.codex/skills/codex-pr-body/SKILL.md deleted file mode 100644 index 76b37b8..0000000 --- a/reference/openai-codex/.codex/skills/codex-pr-body/SKILL.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -name: codex-pr-body -description: Update the title and body of one or more pull requests. ---- - -## Determining the PR(s) - -When this skill is invoked, the PR(s) to update may be specified explicitly, but in the common case, the PR(s) to update will be inferred from the branch / commit that the user is currently working on. For ordinary Git usage (i.e., not Sapling as discussed below), you may have to use a combination of `git branch` and `gh pr view --repo openai/codex --json number --jq '.number'` to determine the PR associated with the current branch / commit. - -## PR Body Contents - -When invoked, use `gh` to edit the pull request body and title to reflect the contents of the specified PR. Make sure to check the existing pull request body to see if there is key information that should be preserved. For example, NEVER remove an image in the existing pull request body, as the author may have no way to recover it if you remove it. - -It is critically important to explain _why_ the change is being made. If the current conversation in which this skill is invoked has discussed the motivation, be sure to capture this in the pull request body. - -The body should also explain _what_ changed, but this should appear after the _why_. - -Limit discussion to the _net change_ of the commit. It is generally frowned upon to discuss changes that were attempted but later undone in the course of the development of the pull request. When rewriting the pull request body, you may need to eliminate details such as these when they are no longer appropriate / of interest to future readers. - -Avoid references to absolute paths on my local disk. When talking about a path that is within the repository, simply use the repo-relative path. - -It is generally helpful to discuss how the change was verified. That said, it is unnecessary to mention things that CI checks automatically, e.g., do not include "ran `just fmt`" as part of the test plan. Though identifying the new tests that were purposely introduced to verify the new behavior introduced by the pull request is often appropriate. - -Make use of Markdown to format the pull request professionally. Ensure "code things" appear in single backticks when referenced inline. Fenced code blocks are useful when referencing code or showing a shell transcript. Also, make use of GitHub permalinks when citing existing pieces of code that are relevant to the change. - -Make sure to reference any relevant pull requests or issues, though there should be no need to reference the pull request in its own PR body. - -If there is documentation that should be updated on https://developers.openai.com/codex as a result of this change, please note that in a separate section near the end of the pull request. Omit this section if there is no documentation that needs to be updated. - -## Working with Stacks - -Sometimes a pull request is composed of a stack of commits that build on one another. In these cases, the PR body should reflect the _net_ change introduced by the stack as a whole, rather than the individual commits that make up the stack. - -Similarly, sometimes a user may be using a tool like Sapling to leverage _stacked pull requests_, in which case the `base` of the PR may be the a branch that is the `head` of another PR in the stack rather than `main`. In this case, be sure to discuss only the net change between the `base` and `head` of the PR that is being opened against that stacked base, rather than the changes relative to `main`. - -## Sapling - -If `.git/sl/store` is present, then this Git repository is governed by Sapling SCM (https://sapling-scm.com). - -In Sapling, run the following to see if there is a GitHub pull request associated with the current revision: - -```shell -sl log --template '{github_pull_request_url}' -r . -``` - -Alternatively, you can run `sl sl` to see the current development branch and whether there is a GitHub pull request associated with the current commit. For example, if the output were: - -``` - @ cb032b31cf 72 minutes ago mbolin #11412 -╭─╯ tui: show non-file layer content in /debug-config -│ -o fdd0cd1de9 Today at 20:09 origin/main -│ -~ -``` - -- `@` indicates the current commit is `cb032b31cf` -- it is a development branch containing a single commit branched off of `origin/main` -- it is associated with GitHub pull request #11412 diff --git a/reference/openai-codex/.codex/skills/remote-tests/SKILL.md b/reference/openai-codex/.codex/skills/remote-tests/SKILL.md deleted file mode 100644 index ee35fc2..0000000 --- a/reference/openai-codex/.codex/skills/remote-tests/SKILL.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: remote-tests -description: How to run tests using remote executor. ---- - -Some codex integration tests support a running against a remote executor. -This means that when CODEX_TEST_REMOTE_ENV environment variable is set they will attempt to start an executor process in a docker container CODEX_TEST_REMOTE_ENV points to and use it in tests. - -Docker container is built and initialized via ./scripts/test-remote-env.sh - -Currently running remote tests is only supported on Linux, so you need to use a devbox to run them - -You can list devboxes via `applied_devbox ls`, pick the one with `codex` in the name. -Connect to devbox via `ssh `. -Reuse the same checkout of codex in `~/code/codex`. Reset files if needed. Multiple checkouts take longer to build and take up more space. -Check whether the SHA and modified files are in sync between remote and local. diff --git a/reference/openai-codex/.codex/skills/test-tui/SKILL.md b/reference/openai-codex/.codex/skills/test-tui/SKILL.md deleted file mode 100644 index e58e677..0000000 --- a/reference/openai-codex/.codex/skills/test-tui/SKILL.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: test-tui -description: Guide for testing Codex TUI interactively ---- - -You can start and use Codex TUI to verify changes. - -Important notes: - -Start interactively. -Always set RUST_LOG="trace" when starting the process. -Pass `-c log_dir=` argument to have logs written to a specific directory to help with debugging. -When sending a test message programmatically, send text first, then send Enter in a separate write (do not send text + Enter in one burst). -Use `just codex` target to run - `just codex -c ...` diff --git a/reference/openai-codex/.codex/skills/update-v8-version/SKILL.md b/reference/openai-codex/.codex/skills/update-v8-version/SKILL.md deleted file mode 100644 index 512cc0d..0000000 --- a/reference/openai-codex/.codex/skills/update-v8-version/SKILL.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -name: update-v8-version -description: Update Codex's pinned `v8` / `rusty_v8` versions, validate the release-candidate path, and investigate failed V8 canary or artifact builds. Use when asked to bump V8, update `rusty_v8` artifacts, prepare or validate a V8 release candidate, check `v8-canary`, or diagnose why a V8 version update no longer builds. ---- - -# Update V8 Version - -## Core Workflow - -1. Read `third_party/v8/README.md` and follow its version-bump sequence. Treat - that document as the release-process source of truth. -2. Inspect and update the concrete repo surfaces that carry the pin: - - `codex-rs/Cargo.toml` - - `codex-rs/Cargo.lock` - - `MODULE.bazel` - - `third_party/v8/BUILD.bazel` - - `third_party/v8/README.md` - - the matching `third_party/v8/rusty_v8_.sha256` manifest when the - remaining prebuilt inputs change -3. Keep the existing checksum helpers in the loop: - - ```bash - python3 .github/scripts/rusty_v8_bazel.py update-module-bazel - python3 .github/scripts/rusty_v8_bazel.py check-module-bazel - python3 -m unittest discover -s .github/scripts -p test_rusty_v8_bazel.py - ``` - -4. Validate the release-candidate path before broadening the work: - - Prefer checking the `v8-canary` CI result for the candidate branch or PR - when one exists, using GitHub check tooling or `gh` as appropriate. - - If CI is unavailable or the user asked for a local-only check, run the - closest local validation that is practical for the changed surface and say - explicitly that it is a local substitute, not the full hosted canary. -5. If the canary path passes, stop there. Summarize the result and encourage the - user to commit the candidate changes or proceed with the release flow they - requested. Do not publish tags, releases, or pushes unless the user asked. - -## Failure Path - -Enter this path only when the canary or local build path fails. - -1. Capture the failing target, workflow job, and first actionable error. -2. Compare the currently pinned version with the target version at the relevant - upstream tag or SHA. Inspect both: - - `denoland/rusty_v8` - - upstream V8 source at the target Bazel-pinned version -3. Track build-relevant deltas rather than broad source churn: - - generated binding layout changes - - archive or asset naming changes - - GN/Bazel target changes - - custom libc++ / libc++abi / llvm-libc inputs - - sandbox or pointer-compression feature relationships - - patch hunks in `patches/` that no longer apply or no longer match upstream -4. Trace each failing delta back into Codex's build graph: - - `MODULE.bazel` - - `third_party/v8/BUILD.bazel` - - `.github/scripts/rusty_v8_bazel.py` - - `.github/workflows/v8-canary.yml` - - `.github/workflows/rusty-v8-release.yml` -5. Update only the pieces required to restore the target version's build and - artifact contract. Keep patch explanations and doc changes close to the - affected files. -6. Re-run the focused validation. If it becomes green, return to the normal - workflow and stop with a concise summary plus the remaining release step. - -## Reporting - -- Say whether validation came from hosted `v8-canary` or from a local - substitute. -- Distinguish "version bump complete" from "release published". -- When blocked, report the upstream delta that matters, the Codex file it hits, - and the next concrete fix to try. diff --git a/reference/openai-codex/.codex/skills/update-v8-version/agents/openai.yaml b/reference/openai-codex/.codex/skills/update-v8-version/agents/openai.yaml deleted file mode 100644 index 36e7af8..0000000 --- a/reference/openai-codex/.codex/skills/update-v8-version/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Update V8 Version" - short_description: "Guide V8 bumps and release validation" - default_prompt: "Use $update-v8-version to update Codex to a new v8 release and validate the release-candidate path." diff --git a/reference/openai-codex/.devcontainer/Dockerfile b/reference/openai-codex/.devcontainer/Dockerfile deleted file mode 100644 index 8c3a859..0000000 --- a/reference/openai-codex/.devcontainer/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM ubuntu:24.04 - -ARG DEBIAN_FRONTEND=noninteractive -# enable 'universe' because musl-tools & clang live there -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - software-properties-common && \ - add-apt-repository --yes universe - -# now install build deps -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - build-essential curl git ca-certificates \ - pkg-config libcap-dev clang musl-tools libssl-dev just && \ - rm -rf /var/lib/apt/lists/* - -# Ubuntu 24.04 ships with user 'ubuntu' already created with UID 1000. -USER ubuntu - -# install Rust + musl target as dev user -RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal && \ - ~/.cargo/bin/rustup target add aarch64-unknown-linux-musl && \ - ~/.cargo/bin/rustup component add clippy rustfmt - -ENV PATH="/home/ubuntu/.cargo/bin:${PATH}" - -WORKDIR /workspace diff --git a/reference/openai-codex/.devcontainer/Dockerfile.secure b/reference/openai-codex/.devcontainer/Dockerfile.secure deleted file mode 100644 index 6c1878e..0000000 --- a/reference/openai-codex/.devcontainer/Dockerfile.secure +++ /dev/null @@ -1,82 +0,0 @@ -FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 - -ARG TZ -ARG DEBIAN_FRONTEND=noninteractive -ARG NODE_MAJOR=22 -ARG RUST_TOOLCHAIN=1.92.0 -# Keep this in sync with .devcontainer/codex-install/package.json and pnpm-lock.yaml. -ARG CODEX_NPM_VERSION=0.121.0 - -ENV TZ="$TZ" -ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 - -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -# Devcontainers run as a non-root user, so enable bubblewrap's setuid mode. -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - build-essential \ - curl \ - git \ - ca-certificates \ - pkg-config \ - clang \ - musl-tools \ - libssl-dev \ - libsqlite3-dev \ - just \ - python3 \ - python3-pip \ - jq \ - less \ - man-db \ - unzip \ - ripgrep \ - fzf \ - fd-find \ - zsh \ - dnsutils \ - iproute2 \ - ipset \ - iptables \ - aggregate \ - bubblewrap \ - && chmod u+s /usr/bin/bwrap \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -COPY .devcontainer/codex-install/package.json \ - .devcontainer/codex-install/pnpm-lock.yaml \ - .devcontainer/codex-install/pnpm-workspace.yaml \ - /opt/codex-install/ - -RUN curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - \ - && apt-get update \ - && apt-get install -y --no-install-recommends nodejs \ - && test "$(node -p "require('/opt/codex-install/package.json').dependencies['@openai/codex']")" = "${CODEX_NPM_VERSION}" \ - && cd /opt/codex-install \ - && corepack pnpm install --prod --frozen-lockfile \ - && ln -s /opt/codex-install/node_modules/.bin/codex /usr/local/bin/codex \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -COPY .devcontainer/init-firewall.sh /usr/local/bin/init-firewall.sh -COPY .devcontainer/post_install.py /opt/post_install.py -COPY .devcontainer/post-start.sh /opt/post_start.sh - -RUN chmod 500 /usr/local/bin/init-firewall.sh \ - && chmod 755 /opt/post_start.sh \ - && chmod 644 /opt/post_install.py \ - && chown vscode:vscode /opt/post_install.py - -RUN install -d -m 0775 -o vscode -g vscode /commandhistory /workspace \ - && touch /commandhistory/.bash_history /commandhistory/.zsh_history \ - && chown vscode:vscode /commandhistory/.bash_history /commandhistory/.zsh_history - -USER vscode -ENV PATH="/home/vscode/.cargo/bin:${PATH}" -WORKDIR /workspace - -RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "${RUST_TOOLCHAIN}" \ - && rustup component add clippy rustfmt rust-src \ - && rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl diff --git a/reference/openai-codex/.devcontainer/README.md b/reference/openai-codex/.devcontainer/README.md deleted file mode 100644 index 3b07f2b..0000000 --- a/reference/openai-codex/.devcontainer/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Containerized Development - -We provide two container paths: - -- `devcontainer.json` keeps the existing Codex contributor setup for working on this repository. -- `devcontainer.secure.json` adds a customer-oriented profile with stricter outbound network controls. - -## Codex contributor profile - -Use `devcontainer.json` when you are developing Codex itself. This is the same lightweight arm64 container that already exists in the repo. - -## Secure customer profile - -Use `devcontainer.secure.json` when you want a stricter runtime profile for running Codex inside a project container: - -- installs the Codex CLI plus common build tools -- installs bubblewrap in setuid mode for Codex's Linux sandbox -- disables Docker's outer seccomp and AppArmor profiles so bubblewrap can construct Codex's inner sandbox -- enables firewall startup with an allowlist-driven outbound policy -- blocks IPv6 by default so the allowlist cannot be bypassed over AAAA routes -- requires `NET_ADMIN` and `NET_RAW` so the firewall can be installed at startup - -This profile keeps the stricter networking isolated to the customer path instead of changing the default Codex contributor container. - -Start it from the CLI with: - -```bash -devcontainer up --workspace-folder . --config .devcontainer/devcontainer.secure.json -``` - -In VS Code, choose **Dev Containers: Open Folder in Container...** and select `.devcontainer/devcontainer.secure.json`. - -## Docker - -To build the contributor image locally for x64 and then run it with the repo mounted under `/workspace`: - -```shell -CODEX_DOCKER_IMAGE_NAME=codex-linux-dev -docker build --platform=linux/amd64 -t "$CODEX_DOCKER_IMAGE_NAME" ./.devcontainer -docker run --platform=linux/amd64 --rm -it -e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64 -v "$PWD":/workspace -w /workspace/codex-rs "$CODEX_DOCKER_IMAGE_NAME" -``` - -Note that `/workspace/target` will contain the binaries built for your host platform, so we include `-e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64` in the `docker run` command so that the binaries built inside your container are written to a separate directory. - -For arm64, specify `--platform=linux/arm64` instead for both `docker build` and `docker run`. - -Currently, the contributor `Dockerfile` works for both x64 and arm64 Linux, though you need to run `rustup target add x86_64-unknown-linux-musl` yourself to install the musl toolchain for x64. - -The secure profile's capability, seccomp, and AppArmor options are required when you want Codex's bubblewrap sandbox to run inside Docker as the non-root devcontainer user. Without them, Docker's default runtime profile can block bubblewrap's namespace setup before Codex's own seccomp filter is installed. This keeps the Docker relaxation explicit in the profile that is meant to run Codex inside a project container, while the default contributor profile stays lightweight. diff --git a/reference/openai-codex/.devcontainer/codex-install/package.json b/reference/openai-codex/.devcontainer/codex-install/package.json deleted file mode 100644 index 453054e..0000000 --- a/reference/openai-codex/.devcontainer/codex-install/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "codex-devcontainer-install", - "private": true, - "description": "Locked Codex CLI install boundary for the secure devcontainer.", - "dependencies": { - "@openai/codex": "0.121.0" - }, - "engines": { - "node": ">=22", - "pnpm": ">=10.33.0" - }, - "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319" -} diff --git a/reference/openai-codex/.devcontainer/codex-install/pnpm-lock.yaml b/reference/openai-codex/.devcontainer/codex-install/pnpm-lock.yaml deleted file mode 100644 index 70e7608..0000000 --- a/reference/openai-codex/.devcontainer/codex-install/pnpm-lock.yaml +++ /dev/null @@ -1,85 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@openai/codex': - specifier: 0.121.0 - version: 0.121.0 - -packages: - - '@openai/codex@0.121.0': - resolution: {integrity: sha512-kCJ2NeATd4QBQRmqV04ymdN1ZU3MSwnJQDm/KzjpuzGvCuUVEn7no/T2mRyxQ2x77AACqriNOyPPoM/yufyvNg==} - engines: {node: '>=16'} - hasBin: true - - '@openai/codex@0.121.0-darwin-arm64': - resolution: {integrity: sha512-ZyBqIB6Fb4I0hGb/h65Vu7ePYjHSmGiqqfm+/1djEuxDPkqjfi4wkxYxNYNY+6najyNGN4UijOSTTf19eDCrqw==} - engines: {node: '>=16'} - cpu: [arm64] - os: [darwin] - - '@openai/codex@0.121.0-darwin-x64': - resolution: {integrity: sha512-1/OAtdkAZ5yPI3xqaEFlHuPziS1yCqL2gOZdswE7HTmmwpIxi6Z3FCo60JWDPluIp89z4tftdjq73/OCN0YVcw==} - engines: {node: '>=16'} - cpu: [x64] - os: [darwin] - - '@openai/codex@0.121.0-linux-arm64': - resolution: {integrity: sha512-2UgMmdo237o7SCMsfb529cOSEM2HFUgN6OBkv5SBLwfNY1NO2Ex6JnUjlppEXlX6/4cXfZ5qjDghVz5j/+B9zw==} - engines: {node: '>=16'} - cpu: [arm64] - os: [linux] - - '@openai/codex@0.121.0-linux-x64': - resolution: {integrity: sha512-vlpNJXIqss800J+32Vy7TUZzv31n61b45OLxmsVQGFkTNLJcjFrj9jDUC7I62eC4F16gLioilefNfv4CdJQOEw==} - engines: {node: '>=16'} - cpu: [x64] - os: [linux] - - '@openai/codex@0.121.0-win32-arm64': - resolution: {integrity: sha512-m88q4f3XI5npn1t6OG0nWGHWWAjO5FgjRwxh4hdujbLO6t9CiCNfhfPZIOSsoATbrCNwLC+6S77m3cjbNToPNg==} - engines: {node: '>=16'} - cpu: [arm64] - os: [win32] - - '@openai/codex@0.121.0-win32-x64': - resolution: {integrity: sha512-Fp0ecVOyM+VcBi/y4HVvRzhifO9YqRiHzhV3rhtAppC7flh22WPguLC4kmvXYAR0p3RPzbo35M2CedWnkOT+cw==} - engines: {node: '>=16'} - cpu: [x64] - os: [win32] - -snapshots: - - '@openai/codex@0.121.0': - optionalDependencies: - '@openai/codex-darwin-arm64': '@openai/codex@0.121.0-darwin-arm64' - '@openai/codex-darwin-x64': '@openai/codex@0.121.0-darwin-x64' - '@openai/codex-linux-arm64': '@openai/codex@0.121.0-linux-arm64' - '@openai/codex-linux-x64': '@openai/codex@0.121.0-linux-x64' - '@openai/codex-win32-arm64': '@openai/codex@0.121.0-win32-arm64' - '@openai/codex-win32-x64': '@openai/codex@0.121.0-win32-x64' - - '@openai/codex@0.121.0-darwin-arm64': - optional: true - - '@openai/codex@0.121.0-darwin-x64': - optional: true - - '@openai/codex@0.121.0-linux-arm64': - optional: true - - '@openai/codex@0.121.0-linux-x64': - optional: true - - '@openai/codex@0.121.0-win32-arm64': - optional: true - - '@openai/codex@0.121.0-win32-x64': - optional: true diff --git a/reference/openai-codex/.devcontainer/codex-install/pnpm-workspace.yaml b/reference/openai-codex/.devcontainer/codex-install/pnpm-workspace.yaml deleted file mode 100644 index 3b901a0..0000000 --- a/reference/openai-codex/.devcontainer/codex-install/pnpm-workspace.yaml +++ /dev/null @@ -1,12 +0,0 @@ -packages: - - "." - -minimumReleaseAge: 10080 -minimumReleaseAgeExclude: [] - -blockExoticSubdeps: true -strictDepBuilds: true -trustPolicy: no-downgrade -trustPolicyIgnoreAfter: 10080 -trustPolicyExclude: [] -allowBuilds: {} diff --git a/reference/openai-codex/.devcontainer/devcontainer.json b/reference/openai-codex/.devcontainer/devcontainer.json deleted file mode 100644 index 1bed79c..0000000 --- a/reference/openai-codex/.devcontainer/devcontainer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "Codex", - "build": { - "dockerfile": "Dockerfile", - "context": "..", - "platform": "linux/arm64" - }, - - /* Force VS Code to run the container as arm64 in - case your host is x86 (or vice-versa). */ - "runArgs": ["--platform=linux/arm64"], - - "containerEnv": { - "RUST_BACKTRACE": "1", - "CARGO_TARGET_DIR": "${containerWorkspaceFolder}/codex-rs/target-arm64" - }, - - "remoteUser": "ubuntu", - "customizations": { - "vscode": { - "settings": { - "terminal.integrated.defaultProfile.linux": "bash" - }, - "extensions": ["rust-lang.rust-analyzer", "tamasfe.even-better-toml"] - } - } -} diff --git a/reference/openai-codex/.devcontainer/devcontainer.secure.json b/reference/openai-codex/.devcontainer/devcontainer.secure.json deleted file mode 100644 index 5d5808e..0000000 --- a/reference/openai-codex/.devcontainer/devcontainer.secure.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json", - "name": "Codex (Secure)", - "build": { - "dockerfile": "Dockerfile.secure", - "context": "..", - "args": { - "TZ": "${localEnv:TZ:UTC}", - "NODE_MAJOR": "22", - "RUST_TOOLCHAIN": "1.92.0", - "CODEX_NPM_VERSION": "0.121.0" - } - }, - "runArgs": [ - "--cap-add=SYS_ADMIN", - "--cap-add=SYS_CHROOT", - "--cap-add=SETUID", - "--cap-add=SETGID", - "--cap-add=SYS_PTRACE", - "--security-opt=seccomp=unconfined", - "--security-opt=apparmor=unconfined", - "--cap-add=NET_ADMIN", - "--cap-add=NET_RAW" - ], - "init": true, - "updateRemoteUserUID": true, - "remoteUser": "vscode", - "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated", - "workspaceFolder": "/workspace", - "mounts": [ - "source=codex-commandhistory-${devcontainerId},target=/commandhistory,type=volume", - "source=codex-home-${devcontainerId},target=/home/vscode/.codex,type=volume", - "source=codex-gh-${devcontainerId},target=/home/vscode/.config/gh,type=volume", - "source=codex-cargo-registry-${devcontainerId},target=/home/vscode/.cargo/registry,type=volume", - "source=codex-cargo-git-${devcontainerId},target=/home/vscode/.cargo/git,type=volume", - "source=codex-rustup-${devcontainerId},target=/home/vscode/.rustup,type=volume", - "source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,readonly" - ], - "containerEnv": { - "RUST_BACKTRACE": "1", - "CODEX_UNSAFE_ALLOW_NO_SANDBOX": "1", - "CODEX_ENABLE_FIREWALL": "1", - "CODEX_INCLUDE_GITHUB_META_RANGES": "1", - "OPENAI_ALLOWED_DOMAINS": "api.openai.com auth.openai.com github.com api.github.com codeload.github.com raw.githubusercontent.com objects.githubusercontent.com crates.io index.crates.io static.crates.io static.rust-lang.org registry.npmjs.org pypi.org files.pythonhosted.org", - "CARGO_TARGET_DIR": "/workspace/.cache/cargo-target", - "GIT_CONFIG_GLOBAL": "/home/vscode/.gitconfig.local", - "COREPACK_ENABLE_DOWNLOAD_PROMPT": "0", - "PYTHONDONTWRITEBYTECODE": "1", - "PIP_DISABLE_PIP_VERSION_CHECK": "1" - }, - "remoteEnv": { - "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}" - }, - "postCreateCommand": "python3 /opt/post_install.py", - "postStartCommand": "bash /opt/post_start.sh", - "waitFor": "postStartCommand", - "customizations": { - "vscode": { - "settings": { - "terminal.integrated.defaultProfile.linux": "zsh", - "terminal.integrated.profiles.linux": { - "bash": { - "path": "bash", - "icon": "terminal-bash" - }, - "zsh": { - "path": "zsh" - } - }, - "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true, - "files.trimFinalNewlines": true - }, - "extensions": [ - "openai.chatgpt", - "rust-lang.rust-analyzer", - "tamasfe.even-better-toml", - "vadimcn.vscode-lldb", - "ms-azuretools.vscode-docker" - ] - } - } -} diff --git a/reference/openai-codex/.devcontainer/init-firewall.sh b/reference/openai-codex/.devcontainer/init-firewall.sh deleted file mode 100644 index 9275724..0000000 --- a/reference/openai-codex/.devcontainer/init-firewall.sh +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -allowed_domains_file="/etc/codex/allowed_domains.txt" -include_github_meta_ranges="${CODEX_INCLUDE_GITHUB_META_RANGES:-1}" - -if [ -f "$allowed_domains_file" ]; then - mapfile -t allowed_domains < <(sed '/^\s*#/d;/^\s*$/d' "$allowed_domains_file") -else - allowed_domains=("api.openai.com") -fi - -if [ "${#allowed_domains[@]}" -eq 0 ]; then - echo "ERROR: No allowed domains configured" - exit 1 -fi - -add_ipv4_cidr_to_allowlist() { - local source="$1" - local cidr="$2" - - if [[ ! "$cidr" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}/[0-9]{1,2}$ ]]; then - echo "ERROR: Invalid ${source} CIDR range: $cidr" - exit 1 - fi - - ipset add allowed-domains "$cidr" -exist -} - -configure_ipv6_default_deny() { - if ! command -v ip6tables >/dev/null 2>&1; then - echo "ERROR: ip6tables is required to enforce IPv6 default-deny policy" - exit 1 - fi - - ip6tables -F - ip6tables -X - ip6tables -t mangle -F - ip6tables -t mangle -X - ip6tables -t nat -F 2>/dev/null || true - ip6tables -t nat -X 2>/dev/null || true - - ip6tables -A INPUT -i lo -j ACCEPT - ip6tables -A OUTPUT -o lo -j ACCEPT - ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT - ip6tables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT - - ip6tables -P INPUT DROP - ip6tables -P FORWARD DROP - ip6tables -P OUTPUT DROP - - echo "IPv6 firewall policy configured (default-deny)" -} - -# Preserve docker-managed DNS NAT rules before clearing tables. -docker_dns_rules="$(iptables-save -t nat | grep "127\\.0\\.0\\.11" || true)" - -iptables -F -iptables -X -iptables -t nat -F -iptables -t nat -X -iptables -t mangle -F -iptables -t mangle -X -ipset destroy allowed-domains 2>/dev/null || true - -if [ -n "$docker_dns_rules" ]; then - echo "Restoring Docker DNS NAT rules" - iptables -t nat -N DOCKER_OUTPUT 2>/dev/null || true - iptables -t nat -N DOCKER_POSTROUTING 2>/dev/null || true - while IFS= read -r rule; do - [ -z "$rule" ] && continue - iptables -t nat $rule - done <<< "$docker_dns_rules" -fi - -# Allow DNS resolution and localhost communication. -iptables -A OUTPUT -p udp --dport 53 -j ACCEPT -iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT -iptables -A INPUT -p udp --sport 53 -j ACCEPT -iptables -A INPUT -p tcp --sport 53 -j ACCEPT -iptables -A INPUT -i lo -j ACCEPT -iptables -A OUTPUT -o lo -j ACCEPT - -ipset create allowed-domains hash:net - -for domain in "${allowed_domains[@]}"; do - echo "Resolving $domain" - ips="$(dig +short A "$domain" | sed '/^\s*$/d')" - if [ -z "$ips" ]; then - echo "ERROR: Failed to resolve $domain" - exit 1 - fi - - while IFS= read -r ip; do - if [[ ! "$ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then - echo "ERROR: Invalid IPv4 address from DNS for $domain: $ip" - exit 1 - fi - ipset add allowed-domains "$ip" -exist - done <<< "$ips" -done - -if [ "$include_github_meta_ranges" = "1" ]; then - echo "Fetching GitHub meta ranges" - github_meta="$(curl -fsSL --connect-timeout 10 https://api.github.com/meta)" - - if ! echo "$github_meta" | jq -e '.web and .api and .git' >/dev/null; then - echo "ERROR: GitHub meta response missing expected fields" - exit 1 - fi - - while IFS= read -r cidr; do - [ -z "$cidr" ] && continue - if [[ "$cidr" == *:* ]]; then - # Current policy enforces IPv4-only ipset entries. - continue - fi - add_ipv4_cidr_to_allowlist "GitHub" "$cidr" - done < <(echo "$github_meta" | jq -r '((.web // []) + (.api // []) + (.git // []))[]' | sort -u) -fi - -host_ip="$(ip route | awk '/default/ {print $3; exit}')" -if [ -z "$host_ip" ]; then - echo "ERROR: Failed to detect host IP" - exit 1 -fi - -host_network="$(echo "$host_ip" | sed 's/\.[0-9]*$/.0\/24/')" -iptables -A INPUT -s "$host_network" -j ACCEPT -iptables -A OUTPUT -d "$host_network" -j ACCEPT - -iptables -P INPUT DROP -iptables -P FORWARD DROP -iptables -P OUTPUT DROP - -iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT -iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT -iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT - -# Reject rather than silently drop to make policy failures obvious. -iptables -A INPUT -j REJECT --reject-with icmp-admin-prohibited -iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited -iptables -A FORWARD -j REJECT --reject-with icmp-admin-prohibited - -configure_ipv6_default_deny - -echo "Firewall configuration complete" - -if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then - echo "ERROR: Firewall verification failed - was able to reach https://example.com" - exit 1 -fi - -if ! curl --connect-timeout 5 https://api.openai.com >/dev/null 2>&1; then - echo "ERROR: Firewall verification failed - unable to reach https://api.openai.com" - exit 1 -fi - -if [ "$include_github_meta_ranges" = "1" ] && ! curl --connect-timeout 5 https://api.github.com/zen >/dev/null 2>&1; then - echo "ERROR: Firewall verification failed - unable to reach https://api.github.com" - exit 1 -fi - -if curl --connect-timeout 5 -6 https://example.com >/dev/null 2>&1; then - echo "ERROR: Firewall verification failed - was able to reach https://example.com over IPv6" - exit 1 -fi - -echo "Firewall verification passed" diff --git a/reference/openai-codex/.devcontainer/post-start.sh b/reference/openai-codex/.devcontainer/post-start.sh deleted file mode 100644 index fcc42ad..0000000 --- a/reference/openai-codex/.devcontainer/post-start.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -if [ "${CODEX_ENABLE_FIREWALL:-1}" != "1" ]; then - echo "[devcontainer] Firewall mode: permissive (CODEX_ENABLE_FIREWALL=${CODEX_ENABLE_FIREWALL:-unset})." - exit 0 -fi - -echo "[devcontainer] Firewall mode: strict" - -domains_raw="${OPENAI_ALLOWED_DOMAINS:-api.openai.com}" -mapfile -t domains < <(printf '%s\n' "$domains_raw" | tr ', ' '\n\n' | sed '/^$/d' | sort -u) - -if [ "${#domains[@]}" -eq 0 ]; then - echo "[devcontainer] No allowed domains configured." - exit 1 -fi - -tmp_file="$(mktemp)" -for domain in "${domains[@]}"; do - if [[ ! "$domain" =~ ^[a-zA-Z0-9][a-zA-Z0-9.-]*\.[a-zA-Z]{2,}$ ]]; then - echo "[devcontainer] Invalid domain in OPENAI_ALLOWED_DOMAINS: $domain" - rm -f "$tmp_file" - exit 1 - fi - printf '%s\n' "$domain" >> "$tmp_file" -done - -sudo install -d -m 0755 /etc/codex -sudo cp "$tmp_file" /etc/codex/allowed_domains.txt -sudo chown root:root /etc/codex/allowed_domains.txt -sudo chmod 0444 /etc/codex/allowed_domains.txt -rm -f "$tmp_file" - -echo "[devcontainer] Applying firewall policy for domains: ${domains[*]}" -sudo --preserve-env=CODEX_INCLUDE_GITHUB_META_RANGES /usr/local/bin/init-firewall.sh diff --git a/reference/openai-codex/.devcontainer/post_install.py b/reference/openai-codex/.devcontainer/post_install.py deleted file mode 100644 index 205e57c..0000000 --- a/reference/openai-codex/.devcontainer/post_install.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python3 -"""Post-install configuration for the Codex devcontainer.""" - -from __future__ import annotations - -import os -import subprocess -import sys -from pathlib import Path - - -def ensure_history_files() -> None: - command_history_dir = Path("/commandhistory") - command_history_dir.mkdir(parents=True, exist_ok=True) - - for filename in (".bash_history", ".zsh_history"): - (command_history_dir / filename).touch(exist_ok=True) - - -def fix_directory_ownership() -> None: - uid = os.getuid() - gid = os.getgid() - - paths = [ - Path.home() / ".codex", - Path.home() / ".config" / "gh", - Path.home() / ".cargo", - Path.home() / ".rustup", - Path("/commandhistory"), - ] - - for path in paths: - if not path.exists(): - continue - - stat_info = path.stat() - if stat_info.st_uid == uid and stat_info.st_gid == gid: - continue - - try: - subprocess.run( - ["sudo", "chown", "-R", f"{uid}:{gid}", str(path)], - check=True, - capture_output=True, - text=True, - ) - print(f"[post_install] fixed ownership: {path}", file=sys.stderr) - except subprocess.CalledProcessError as err: - print( - f"[post_install] warning: could not fix ownership of {path}: {err.stderr.strip()}", - file=sys.stderr, - ) - - -def setup_git_config() -> None: - home = Path.home() - host_gitconfig = home / ".gitconfig" - local_gitconfig = home / ".gitconfig.local" - gitignore_global = home / ".gitignore_global" - - gitignore_global.write_text( - """# Codex -.codex/ - -# Rust -/target/ - -# Node -node_modules/ - -# Python -__pycache__/ -*.pyc - -# Editors -.vscode/ -.idea/ - -# macOS -.DS_Store -""", - encoding="utf-8", - ) - - include_line = ( - f"[include]\n path = {host_gitconfig}\n\n" if host_gitconfig.exists() else "" - ) - - local_gitconfig.write_text( - f"""# Container-local git configuration -{include_line}[core] - excludesfile = {gitignore_global} - -[merge] - conflictstyle = diff3 - -[diff] - colorMoved = default -""", - encoding="utf-8", - ) - - -def main() -> None: - print("[post_install] configuring devcontainer...", file=sys.stderr) - ensure_history_files() - fix_directory_ownership() - setup_git_config() - print("[post_install] complete", file=sys.stderr) - - -if __name__ == "__main__": - main() diff --git a/reference/openai-codex/.gitattributes b/reference/openai-codex/.gitattributes deleted file mode 100644 index 57c5fe6..0000000 --- a/reference/openai-codex/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -codex-rs/app-server-protocol/schema/** linguist-generated -codex-rs/hooks/schema/generated/** linguist-generated diff --git a/reference/openai-codex/.github/CODEOWNERS b/reference/openai-codex/.github/CODEOWNERS deleted file mode 100644 index 322aad4..0000000 --- a/reference/openai-codex/.github/CODEOWNERS +++ /dev/null @@ -1,6 +0,0 @@ -# Core crate ownership. -/codex-rs/core/ @openai/codex-core-agent-team -/codex-rs/ext/extension-api/ @openai/codex-core-agent-team - -# Keep ownership changes reviewed by the same team. -/.github/CODEOWNERS @openai/codex-core-agent-team diff --git a/reference/openai-codex/.github/ISSUE_TEMPLATE/1-codex-app.yml b/reference/openai-codex/.github/ISSUE_TEMPLATE/1-codex-app.yml deleted file mode 100644 index 6e294ee..0000000 --- a/reference/openai-codex/.github/ISSUE_TEMPLATE/1-codex-app.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: 🖥️ Codex App Bug -description: Report an issue with the Codex App -labels: - - app -body: - - type: markdown - attributes: - value: | - Before submitting a new issue, please search for existing issues to see if your issue has already been reported. - If it has, please add a 👍 reaction (no need to leave a comment) to the existing issue instead of creating a new one. - - - type: input - id: version - attributes: - label: What version of the Codex App are you using (From “About Codex” dialog)? - validations: - required: true - - type: input - id: plan - attributes: - label: What subscription do you have? - validations: - required: true - - type: input - id: platform - attributes: - label: What platform is your computer? - description: | - For macOS and Linux: copy the output of `uname -mprs` - For Windows: copy the output of `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` in the PowerShell console - - type: textarea - id: actual - attributes: - label: What issue are you seeing? - description: Please include the full error messages and prompts with PII redacted. If possible, please provide text instead of a screenshot. - validations: - required: true - - type: textarea - id: steps - attributes: - label: What steps can reproduce the bug? - description: Explain the bug and provide a code snippet that can reproduce it. Please include session id, token limit usage, context window usage if applicable. - validations: - required: true - - type: textarea - id: expected - attributes: - label: What is the expected behavior? - description: If possible, please provide text instead of a screenshot. - - type: textarea - id: notes - attributes: - label: Additional information - description: Is there anything else you think we should know? diff --git a/reference/openai-codex/.github/ISSUE_TEMPLATE/2-extension.yml b/reference/openai-codex/.github/ISSUE_TEMPLATE/2-extension.yml deleted file mode 100644 index 599bc08..0000000 --- a/reference/openai-codex/.github/ISSUE_TEMPLATE/2-extension.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: 🧑‍💻 IDE Extension Bug -description: Report an issue with the IDE extension -labels: - - extension -body: - - type: markdown - attributes: - value: | - Before submitting a new issue, please search for existing issues to see if your issue has already been reported. - If it has, please add a 👍 reaction (no need to leave a comment) to the existing issue instead of creating a new one. - - - type: input - id: version - attributes: - label: What version of the IDE extension are you using? - validations: - required: true - - type: input - id: plan - attributes: - label: What subscription do you have? - validations: - required: true - - type: input - id: ide - attributes: - label: Which IDE are you using? - description: Like `VS Code`, `Cursor`, `Windsurf`, etc. - validations: - required: true - - type: input - id: platform - attributes: - label: What platform is your computer? - description: | - For macOS and Linux: copy the output of `uname -mprs` - For Windows: copy the output of `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` in the PowerShell console - - type: textarea - id: actual - attributes: - label: What issue are you seeing? - description: Please include the full error messages and prompts with PII redacted. If possible, please provide text instead of a screenshot. - validations: - required: true - - type: textarea - id: steps - attributes: - label: What steps can reproduce the bug? - description: Explain the bug and provide a code snippet that can reproduce it. - validations: - required: true - - type: textarea - id: expected - attributes: - label: What is the expected behavior? - description: If possible, please provide text instead of a screenshot. - - type: textarea - id: notes - attributes: - label: Additional information - description: Is there anything else you think we should know? diff --git a/reference/openai-codex/.github/ISSUE_TEMPLATE/3-cli.yml b/reference/openai-codex/.github/ISSUE_TEMPLATE/3-cli.yml deleted file mode 100644 index cfd368c..0000000 --- a/reference/openai-codex/.github/ISSUE_TEMPLATE/3-cli.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: 💻 CLI Bug -description: Report an issue in the Codex CLI -labels: - - bug -body: - - type: markdown - attributes: - value: | - Before submitting a new issue, please search for existing issues to see if your issue has already been reported. - If it has, please add a 👍 reaction (no need to leave a comment) to the existing issue instead of creating a new one. - - Make sure you are running the [latest](https://npmjs.com/package/@openai/codex) version of Codex CLI. The bug you are experiencing may already have been fixed. - - If your version supports it, please run `codex doctor --json` and paste the output in the "Codex doctor report" field below. This helps us diagnose install, config, auth, terminal, MCP, network, and local state issues. - - - type: input - id: version - attributes: - label: What version of Codex CLI is running? - description: use `codex --version` - validations: - required: true - - type: input - id: plan - attributes: - label: What subscription do you have? - validations: - required: true - - type: input - id: model - attributes: - label: Which model were you using? - description: Like `gpt-5.2`, `gpt-5.2-codex`, etc. - - type: input - id: platform - attributes: - label: What platform is your computer? - description: | - For macOS and Linux: copy the output of `uname -mprs` - For Windows: copy the output of `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` in the PowerShell console - - type: input - id: terminal - attributes: - label: What terminal emulator and version are you using (if applicable)? - description: | - Also note any multiplexer in use (screen / tmux / zellij). - E.g., VS Code, Terminal.app, iTerm2, Ghostty, Windows Terminal (WSL / PowerShell) - - type: textarea - id: doctor - attributes: - label: Codex doctor report - description: | - If available, run `codex doctor --json` and paste the full output here. - - The report is designed to redact secrets, but please review it before submitting. - If your Codex version does not support `doctor`, write `not available`. - render: json - - type: textarea - id: actual - attributes: - label: What issue are you seeing? - description: Please include the full error messages and prompts with PII redacted. If possible, please provide text instead of a screenshot. - validations: - required: true - - type: textarea - id: steps - attributes: - label: What steps can reproduce the bug? - description: Explain the bug and provide a code snippet that can reproduce it. Please include thread id if applicable. - validations: - required: true - - type: textarea - id: expected - attributes: - label: What is the expected behavior? - description: If possible, please provide text instead of a screenshot. - - type: textarea - id: notes - attributes: - label: Additional information - description: Is there anything else you think we should know? diff --git a/reference/openai-codex/.github/ISSUE_TEMPLATE/4-bug-report.yml b/reference/openai-codex/.github/ISSUE_TEMPLATE/4-bug-report.yml deleted file mode 100644 index 4de8841..0000000 --- a/reference/openai-codex/.github/ISSUE_TEMPLATE/4-bug-report.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: 🪲 Other Bug -description: Report an issue in Codex Web, integrations, or other Codex components -labels: - - bug -body: - - type: markdown - attributes: - value: | - Before submitting a new issue, please search for existing issues to see if your issue has already been reported. - If it has, please add a 👍 reaction (no need to leave a comment) to the existing issue instead of creating a new one. - - If you need help or support using Codex and are not reporting a bug, please post on [codex/discussions](https://github.com/openai/codex/discussions), where you can ask questions or engage with others on ideas for how to improve codex. - - - type: textarea - id: actual - attributes: - label: What issue are you seeing? - description: Please include the full error messages and prompts with PII redacted. If possible, please provide text instead of a screenshot. - validations: - required: true - - type: textarea - id: steps - attributes: - label: What steps can reproduce the bug? - description: Explain the bug and provide a code snippet that can reproduce it. - validations: - required: true - - type: textarea - id: expected - attributes: - label: What is the expected behavior? - description: If possible, please provide text instead of a screenshot. - - type: textarea - id: notes - attributes: - label: Additional information - description: Is there anything else you think we should know? diff --git a/reference/openai-codex/.github/ISSUE_TEMPLATE/5-feature-request.yml b/reference/openai-codex/.github/ISSUE_TEMPLATE/5-feature-request.yml deleted file mode 100644 index 745c347..0000000 --- a/reference/openai-codex/.github/ISSUE_TEMPLATE/5-feature-request.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: 🎁 Feature Request -description: Propose a new feature for Codex -labels: - - enhancement -body: - - type: markdown - attributes: - value: | - Is Codex missing a feature that you'd like to see? Feel free to propose it here. - - Before you submit a feature: - 1. Search existing issues for similar features. If you find one, 👍 it rather than opening a new one. - 2. The Codex team will try to balance the varying needs of the community when prioritizing or rejecting new features. Not all features will be accepted. See [Contributing](https://github.com/openai/codex/blob/main/docs/contributing.md) for more details. - - - type: input - id: variant - attributes: - label: What variant of Codex are you using? - description: (e.g., App, IDE Extension, CLI, Web) - validations: - required: true - - type: textarea - id: feature - attributes: - label: What feature would you like to see? - validations: - required: true - - type: textarea - id: notes - attributes: - label: Additional information - description: Is there anything else you think we should know? diff --git a/reference/openai-codex/.github/ISSUE_TEMPLATE/6-docs-issue.yml b/reference/openai-codex/.github/ISSUE_TEMPLATE/6-docs-issue.yml deleted file mode 100644 index 1957b60..0000000 --- a/reference/openai-codex/.github/ISSUE_TEMPLATE/6-docs-issue.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: 📗 Documentation Issue -description: Tell us if there is missing or incorrect documentation -labels: [documentation] -body: - - type: markdown - attributes: - value: | - Thank you for submitting a documentation request. It helps make Codex better. - - type: dropdown - attributes: - label: What is the type of issue? - multiple: true - options: - - Documentation is missing - - Documentation is incorrect - - Documentation is confusing - - Example code is not working - - Something else - - type: textarea - attributes: - label: What is the issue? - validations: - required: true - - type: textarea - attributes: - label: Where did you find it? - description: If possible, please provide the URL(s) where you found this issue. diff --git a/reference/openai-codex/.github/actions/linux-code-sign/action.yml b/reference/openai-codex/.github/actions/linux-code-sign/action.yml deleted file mode 100644 index f8efb82..0000000 --- a/reference/openai-codex/.github/actions/linux-code-sign/action.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: linux-code-sign -description: Sign Linux artifacts with cosign. -inputs: - target: - description: Target triple for the artifacts to sign. - required: true - artifacts-dir: - description: Absolute path to the directory containing built binaries to sign. - required: true - binaries: - description: Space-delimited binary basenames to sign. - default: "codex codex-responses-api-proxy" - -runs: - using: composite - steps: - - name: Install cosign - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0 - - - name: Cosign Linux artifacts - shell: bash - env: - ARTIFACTS_DIR: ${{ inputs.artifacts-dir }} - BINARIES: ${{ inputs.binaries }} - COSIGN_EXPERIMENTAL: "1" - COSIGN_YES: "true" - COSIGN_OIDC_CLIENT_ID: "sigstore" - COSIGN_OIDC_ISSUER: "https://oauth2.sigstore.dev/auth" - run: | - set -euo pipefail - - dest="$ARTIFACTS_DIR" - if [[ ! -d "$dest" ]]; then - echo "Destination $dest does not exist" - exit 1 - fi - - for binary in ${BINARIES}; do - artifact="${dest}/${binary}" - if [[ ! -f "$artifact" ]]; then - echo "Binary $artifact not found" - exit 1 - fi - - cosign sign-blob \ - --yes \ - --bundle "${artifact}.sigstore" \ - "$artifact" - done diff --git a/reference/openai-codex/.github/actions/macos-code-sign/action.yml b/reference/openai-codex/.github/actions/macos-code-sign/action.yml deleted file mode 100644 index 0e19fa1..0000000 --- a/reference/openai-codex/.github/actions/macos-code-sign/action.yml +++ /dev/null @@ -1,259 +0,0 @@ -name: macos-code-sign -description: Configure, sign, notarize, and clean up macOS code signing artifacts. -inputs: - target: - description: Rust compilation target triple (e.g. aarch64-apple-darwin). - required: true - binaries: - description: Space-delimited binary basenames to sign and notarize. - default: "codex codex-responses-api-proxy" - sign-binaries: - description: Whether to sign and notarize the macOS binaries. - required: false - default: "true" - sign-dmg: - description: Whether to sign and notarize the macOS dmg. - required: false - default: "true" - apple-certificate: - description: Base64-encoded Apple signing certificate (P12). - required: true - apple-certificate-password: - description: Password for the signing certificate. - required: true - apple-notarization-key-p8: - description: Base64-encoded Apple notarization key (P8). - required: true - apple-notarization-key-id: - description: Apple notarization key ID. - required: true - apple-notarization-issuer-id: - description: Apple notarization issuer ID. - required: true -runs: - using: composite - steps: - - name: Configure Apple code signing - shell: bash - env: - KEYCHAIN_PASSWORD: actions - APPLE_CERTIFICATE: ${{ inputs.apple-certificate }} - APPLE_CERTIFICATE_PASSWORD: ${{ inputs.apple-certificate-password }} - run: | - set -euo pipefail - - if [[ -z "${APPLE_CERTIFICATE:-}" ]]; then - echo "APPLE_CERTIFICATE is required for macOS signing" - exit 1 - fi - - if [[ -z "${APPLE_CERTIFICATE_PASSWORD:-}" ]]; then - echo "APPLE_CERTIFICATE_PASSWORD is required for macOS signing" - exit 1 - fi - - cert_path="${RUNNER_TEMP}/apple_signing_certificate.p12" - echo "$APPLE_CERTIFICATE" | base64 -d > "$cert_path" - - keychain_path="${RUNNER_TEMP}/codex-signing.keychain-db" - security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path" - security set-keychain-settings -lut 21600 "$keychain_path" - security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path" - - keychain_args=() - cleanup_keychain() { - if ((${#keychain_args[@]} > 0)); then - security list-keychains -s "${keychain_args[@]}" || true - security default-keychain -s "${keychain_args[0]}" || true - else - security list-keychains -s || true - fi - if [[ -f "$keychain_path" ]]; then - security delete-keychain "$keychain_path" || true - fi - } - - while IFS= read -r keychain; do - [[ -n "$keychain" ]] && keychain_args+=("$keychain") - done < <(security list-keychains | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/"//g') - - if ((${#keychain_args[@]} > 0)); then - security list-keychains -s "$keychain_path" "${keychain_args[@]}" - else - security list-keychains -s "$keychain_path" - fi - - security default-keychain -s "$keychain_path" - security import "$cert_path" -k "$keychain_path" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security - security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$keychain_path" > /dev/null - - codesign_hashes=() - while IFS= read -r hash; do - [[ -n "$hash" ]] && codesign_hashes+=("$hash") - done < <(security find-identity -v -p codesigning "$keychain_path" \ - | sed -n 's/.*\([0-9A-F]\{40\}\).*/\1/p' \ - | sort -u) - - if ((${#codesign_hashes[@]} == 0)); then - echo "No signing identities found in $keychain_path" - cleanup_keychain - rm -f "$cert_path" - exit 1 - fi - - if ((${#codesign_hashes[@]} > 1)); then - echo "Multiple signing identities found in $keychain_path:" - printf ' %s\n' "${codesign_hashes[@]}" - cleanup_keychain - rm -f "$cert_path" - exit 1 - fi - - APPLE_CODESIGN_IDENTITY="${codesign_hashes[0]}" - - rm -f "$cert_path" - - echo "APPLE_CODESIGN_IDENTITY=$APPLE_CODESIGN_IDENTITY" >> "$GITHUB_ENV" - echo "APPLE_CODESIGN_KEYCHAIN=$keychain_path" >> "$GITHUB_ENV" - echo "::add-mask::$APPLE_CODESIGN_IDENTITY" - - - name: Sign macOS binaries - if: ${{ inputs.sign-binaries == 'true' }} - shell: bash - env: - TARGET: ${{ inputs.target }} - BINARIES: ${{ inputs.binaries }} - run: | - set -euo pipefail - - if [[ -z "${APPLE_CODESIGN_IDENTITY:-}" ]]; then - echo "APPLE_CODESIGN_IDENTITY is required for macOS signing" - exit 1 - fi - - keychain_args=() - if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" && -f "${APPLE_CODESIGN_KEYCHAIN}" ]]; then - keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}") - fi - - entitlements_path="$GITHUB_ACTION_PATH/codex.entitlements.plist" - - for binary in ${BINARIES}; do - path="codex-rs/target/${TARGET}/release/${binary}" - codesign --force --options runtime --timestamp --entitlements "$entitlements_path" --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$path" - done - - - name: Notarize macOS binaries - if: ${{ inputs.sign-binaries == 'true' }} - shell: bash - env: - TARGET: ${{ inputs.target }} - BINARIES: ${{ inputs.binaries }} - APPLE_NOTARIZATION_KEY_P8: ${{ inputs.apple-notarization-key-p8 }} - APPLE_NOTARIZATION_KEY_ID: ${{ inputs.apple-notarization-key-id }} - APPLE_NOTARIZATION_ISSUER_ID: ${{ inputs.apple-notarization-issuer-id }} - run: | - set -euo pipefail - - for var in APPLE_NOTARIZATION_KEY_P8 APPLE_NOTARIZATION_KEY_ID APPLE_NOTARIZATION_ISSUER_ID; do - if [[ -z "${!var:-}" ]]; then - echo "$var is required for notarization" - exit 1 - fi - done - - notary_key_path="${RUNNER_TEMP}/notarytool.key.p8" - echo "$APPLE_NOTARIZATION_KEY_P8" | base64 -d > "$notary_key_path" - cleanup_notary() { - rm -f "$notary_key_path" - } - trap cleanup_notary EXIT - - source "$GITHUB_ACTION_PATH/notary_helpers.sh" - - notarize_binary() { - local binary="$1" - local source_path="codex-rs/target/${TARGET}/release/${binary}" - local archive_path="${RUNNER_TEMP}/${binary}.zip" - - if [[ ! -f "$source_path" ]]; then - echo "Binary $source_path not found" - exit 1 - fi - - rm -f "$archive_path" - ditto -c -k --keepParent "$source_path" "$archive_path" - - notarize_submission "$binary" "$archive_path" "$notary_key_path" - } - - for binary in ${BINARIES}; do - notarize_binary "${binary}" - done - - - name: Sign and notarize macOS dmg - if: ${{ inputs.sign-dmg == 'true' }} - shell: bash - env: - TARGET: ${{ inputs.target }} - APPLE_NOTARIZATION_KEY_P8: ${{ inputs.apple-notarization-key-p8 }} - APPLE_NOTARIZATION_KEY_ID: ${{ inputs.apple-notarization-key-id }} - APPLE_NOTARIZATION_ISSUER_ID: ${{ inputs.apple-notarization-issuer-id }} - run: | - set -euo pipefail - - for var in APPLE_CODESIGN_IDENTITY APPLE_NOTARIZATION_KEY_P8 APPLE_NOTARIZATION_KEY_ID APPLE_NOTARIZATION_ISSUER_ID; do - if [[ -z "${!var:-}" ]]; then - echo "$var is required" - exit 1 - fi - done - - notary_key_path="${RUNNER_TEMP}/notarytool.key.p8" - echo "$APPLE_NOTARIZATION_KEY_P8" | base64 -d > "$notary_key_path" - cleanup_notary() { - rm -f "$notary_key_path" - } - trap cleanup_notary EXIT - - source "$GITHUB_ACTION_PATH/notary_helpers.sh" - - dmg_name="codex-${TARGET}.dmg" - dmg_path="codex-rs/target/${TARGET}/release/${dmg_name}" - - if [[ ! -f "$dmg_path" ]]; then - echo "dmg $dmg_path not found" - exit 1 - fi - - keychain_args=() - if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" && -f "${APPLE_CODESIGN_KEYCHAIN}" ]]; then - keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}") - fi - - codesign --force --timestamp --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$dmg_path" - notarize_submission "$dmg_name" "$dmg_path" "$notary_key_path" - xcrun stapler staple "$dmg_path" - - - name: Remove signing keychain - if: ${{ always() }} - shell: bash - env: - APPLE_CODESIGN_KEYCHAIN: ${{ env.APPLE_CODESIGN_KEYCHAIN }} - run: | - set -euo pipefail - if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" ]]; then - keychain_args=() - while IFS= read -r keychain; do - [[ "$keychain" == "$APPLE_CODESIGN_KEYCHAIN" ]] && continue - [[ -n "$keychain" ]] && keychain_args+=("$keychain") - done < <(security list-keychains | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/"//g') - if ((${#keychain_args[@]} > 0)); then - security list-keychains -s "${keychain_args[@]}" - security default-keychain -s "${keychain_args[0]}" - fi - - if [[ -f "$APPLE_CODESIGN_KEYCHAIN" ]]; then - security delete-keychain "$APPLE_CODESIGN_KEYCHAIN" - fi - fi diff --git a/reference/openai-codex/.github/actions/macos-code-sign/codex.entitlements.plist b/reference/openai-codex/.github/actions/macos-code-sign/codex.entitlements.plist deleted file mode 100644 index d35e43a..0000000 --- a/reference/openai-codex/.github/actions/macos-code-sign/codex.entitlements.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.cs.allow-jit - - - diff --git a/reference/openai-codex/.github/actions/macos-code-sign/notary_helpers.sh b/reference/openai-codex/.github/actions/macos-code-sign/notary_helpers.sh deleted file mode 100644 index ad9757f..0000000 --- a/reference/openai-codex/.github/actions/macos-code-sign/notary_helpers.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash - -notarize_submission() { - local label="$1" - local path="$2" - local notary_key_path="$3" - - if [[ -z "${APPLE_NOTARIZATION_KEY_ID:-}" || -z "${APPLE_NOTARIZATION_ISSUER_ID:-}" ]]; then - echo "APPLE_NOTARIZATION_KEY_ID and APPLE_NOTARIZATION_ISSUER_ID are required for notarization" - exit 1 - fi - - if [[ -z "$notary_key_path" || ! -f "$notary_key_path" ]]; then - echo "Notary key file $notary_key_path not found" - exit 1 - fi - - if [[ ! -f "$path" ]]; then - echo "Notarization payload $path not found" - exit 1 - fi - - local submission_json - submission_json=$(xcrun notarytool submit "$path" \ - --key "$notary_key_path" \ - --key-id "$APPLE_NOTARIZATION_KEY_ID" \ - --issuer "$APPLE_NOTARIZATION_ISSUER_ID" \ - --output-format json \ - --wait) - - local status submission_id - status=$(printf '%s\n' "$submission_json" | jq -r '.status // "Unknown"') - submission_id=$(printf '%s\n' "$submission_json" | jq -r '.id // ""') - - if [[ -z "$submission_id" ]]; then - echo "Failed to retrieve submission ID for $label" - exit 1 - fi - - echo "::notice title=Notarization::$label submission ${submission_id} completed with status ${status}" - - if [[ "$status" != "Accepted" ]]; then - echo "Notarization failed for ${label} (submission ${submission_id}, status ${status})" - exit 1 - fi -} diff --git a/reference/openai-codex/.github/actions/prepare-bazel-ci/action.yml b/reference/openai-codex/.github/actions/prepare-bazel-ci/action.yml deleted file mode 100644 index b41d80e..0000000 --- a/reference/openai-codex/.github/actions/prepare-bazel-ci/action.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: prepare-bazel-ci -description: Prepare a Bazel CI job with shared setup, repository cache restore, and execution logs. -inputs: - target: - description: Target triple used for setup and cache namespacing. - required: true - cache-scope: - description: Logical namespace used to keep concurrent Bazel jobs from reserving the same repository cache key. - required: true - install-test-prereqs: - description: Install DotSlash for Bazel-backed test jobs. - required: false - default: "false" -outputs: - repository-cache-path: - description: Filesystem path used for the Bazel repository cache. - value: ${{ steps.setup_bazel.outputs.repository-cache-path }} - repository-cache-key: - description: Primary actions/cache key for the Bazel repository cache. - value: ${{ steps.cache_bazel_repository_key.outputs.repository-cache-key }} - repository-cache-hit: - description: Whether the Bazel repository cache restore found an exact key match. - value: ${{ steps.cache_bazel_repository_restore.outputs.cache-hit }} - -runs: - using: composite - steps: - - name: Set up Bazel CI - id: setup_bazel - uses: ./.github/actions/setup-bazel-ci - with: - target: ${{ inputs.target }} - install-test-prereqs: ${{ inputs.install-test-prereqs }} - - - name: Compute bazel repository cache key - id: cache_bazel_repository_key - shell: bash - env: - CACHE_SCOPE: ${{ inputs.cache-scope }} - TARGET: ${{ inputs.target }} - CACHE_HASH: ${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} - run: | - echo "repository-cache-key=bazel-cache-${CACHE_SCOPE}-${TARGET}-${CACHE_HASH}" >> "${GITHUB_OUTPUT}" - echo "repository-cache-restore-key=bazel-cache-${CACHE_SCOPE}-${TARGET}-" >> "${GITHUB_OUTPUT}" - - # Restore the Bazel repository cache explicitly so external dependencies - # do not need to be re-downloaded on every CI run. Keep restore failures - # non-fatal so transient cache-service errors degrade to a cold build - # instead of failing the job. - - name: Restore bazel repository cache - id: cache_bazel_repository_restore - continue-on-error: true - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ steps.setup_bazel.outputs.repository-cache-path }} - key: ${{ steps.cache_bazel_repository_key.outputs.repository-cache-key }} - restore-keys: | - ${{ steps.cache_bazel_repository_key.outputs.repository-cache-restore-key }} - - - name: Set up Bazel execution logs - shell: bash - run: | - mkdir -p "${RUNNER_TEMP}/bazel-execution-logs" - echo "CODEX_BAZEL_EXECUTION_LOG_COMPACT_DIR=${RUNNER_TEMP}/bazel-execution-logs" >> "${GITHUB_ENV}" diff --git a/reference/openai-codex/.github/actions/run-argument-comment-lint/action.yml b/reference/openai-codex/.github/actions/run-argument-comment-lint/action.yml deleted file mode 100644 index 80fb23d..0000000 --- a/reference/openai-codex/.github/actions/run-argument-comment-lint/action.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Run argument comment lint -description: Run argument-comment-lint on codex-rs via Bazel. - -inputs: - target: - description: Runner target passed to setup-bazel-ci. - required: true - buildbuddy-api-key: - description: BuildBuddy API key used by Bazel CI. - required: false - default: "" - -runs: - using: composite - steps: - - uses: ./.github/actions/setup-bazel-ci - with: - target: ${{ inputs.target }} - install-test-prereqs: true - - - name: Install Linux sandbox build dependencies - if: ${{ runner.os == 'Linux' }} - shell: bash - run: | - sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev - - - name: Run argument comment lint on codex-rs via Bazel - if: ${{ runner.os != 'Windows' }} - env: - BUILDBUDDY_API_KEY: ${{ inputs.buildbuddy-api-key }} - shell: bash - run: | - bazel_targets="$(./tools/argument-comment-lint/list-bazel-targets.sh)" - ./.github/scripts/run-bazel-ci.sh \ - -- \ - build \ - --config=argument-comment-lint \ - --keep_going \ - --build_metadata=COMMIT_SHA=${GITHUB_SHA} \ - -- \ - ${bazel_targets} - - - name: Run argument comment lint on codex-rs via Bazel - if: ${{ runner.os == 'Windows' }} - env: - BUILDBUDDY_API_KEY: ${{ inputs.buildbuddy-api-key }} - shell: bash - run: | - ./.github/scripts/run-argument-comment-lint-bazel.sh \ - --config=argument-comment-lint \ - --platforms=//:local_windows \ - --keep_going \ - --build_metadata=COMMIT_SHA=${GITHUB_SHA} diff --git a/reference/openai-codex/.github/actions/setup-bazel-ci/action.yml b/reference/openai-codex/.github/actions/setup-bazel-ci/action.yml deleted file mode 100644 index bb757aa..0000000 --- a/reference/openai-codex/.github/actions/setup-bazel-ci/action.yml +++ /dev/null @@ -1,127 +0,0 @@ -name: setup-bazel-ci -description: Prepare a Bazel CI runner with shared caches and optional test prerequisites. -inputs: - target: - description: Target triple used for cache namespacing. - required: true - install-test-prereqs: - description: Install DotSlash for Bazel-backed test jobs. - required: false - default: "false" -outputs: - repository-cache-path: - description: Filesystem path used for the Bazel repository cache. - value: ${{ steps.configure_bazel_repository_cache.outputs.repository-cache-path }} - -runs: - using: composite - steps: - # Some integration tests rely on DotSlash being installed. - # See https://github.com/openai/codex/pull/7617. - - name: Install DotSlash - if: inputs.install-test-prereqs == 'true' - uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2 - - - name: Make DotSlash available in PATH (Unix) - if: inputs.install-test-prereqs == 'true' && runner.os != 'Windows' - shell: bash - run: cp "$(which dotslash)" /usr/local/bin - - - name: Make DotSlash available in PATH (Windows) - if: inputs.install-test-prereqs == 'true' && runner.os == 'Windows' - shell: pwsh - run: Copy-Item (Get-Command dotslash).Source -Destination "$env:LOCALAPPDATA\Microsoft\WindowsApps\dotslash.exe" - - - name: Set up Bazel - uses: bazel-contrib/setup-bazel@c5acdfb288317d0b5c0bbd7a396a3dc868bb0f86 # 0.19.0 - - - name: Configure Bazel repository cache - id: configure_bazel_repository_cache - shell: pwsh - run: | - # Keep the repository cache under HOME on all runners. Windows `D:\a` - # cache paths match `.bazelrc`, but `actions/cache/restore` currently - # returns HTTP 400 for that path in the Windows clippy job. - $repositoryCachePath = Join-Path $HOME '.cache/bazel-repo-cache' - "repository-cache-path=$repositoryCachePath" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append - "BAZEL_REPOSITORY_CACHE=$repositoryCachePath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - - - name: Configure Bazel output root (Windows) - if: runner.os == 'Windows' - shell: pwsh - run: | - # Use the shortest available drive to reduce argv/path length issues, - # but avoid the drive root because some Windows test launchers mis-handle - # MANIFEST paths there. - $hasDDrive = Test-Path 'D:\' - $bazelOutputUserRoot = if ($hasDDrive) { 'D:\b' } else { 'C:\b' } - $repoContentsCache = Join-Path $env:RUNNER_TEMP "bazel-repo-contents-cache-$env:GITHUB_RUN_ID-$env:GITHUB_JOB" - "BAZEL_OUTPUT_USER_ROOT=$bazelOutputUserRoot" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - "BAZEL_REPO_CONTENTS_CACHE=$repoContentsCache" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - - - name: Expose MSVC SDK environment (Windows) - if: runner.os == 'Windows' - shell: pwsh - run: | - # Bazel exec-side Rust build scripts do not reliably inherit the MSVC developer - # shell on GitHub-hosted Windows runners, so discover the latest VS install and - # ask `VsDevCmd.bat` to materialize the x64/x64 compiler + SDK environment. - $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" - if (-not (Test-Path $vswhere)) { - throw "vswhere.exe not found" - } - - $installPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath 2>$null - if (-not $installPath) { - throw "Could not locate a Visual Studio installation with VC tools" - } - - $vsDevCmd = Join-Path $installPath 'Common7\Tools\VsDevCmd.bat' - if (-not (Test-Path $vsDevCmd)) { - throw "VsDevCmd.bat not found at $vsDevCmd" - } - - # Keep the export surface explicit: these are the paths and SDK roots that the - # MSVC toolchain probes need later when Bazel runs Windows exec-platform build - # scripts such as `aws-lc-sys`. - $varsToExport = @( - 'INCLUDE', - 'LIB', - 'LIBPATH', - 'PATH', - 'UCRTVersion', - 'UniversalCRTSdkDir', - 'VCINSTALLDIR', - 'VCToolsInstallDir', - 'WindowsLibPath', - 'WindowsSdkBinPath', - 'WindowsSdkDir', - 'WindowsSDKLibVersion', - 'WindowsSDKVersion' - ) - - # `VsDevCmd.bat` is a batch file, so invoke it under `cmd.exe`, suppress its - # banner, then dump the resulting environment with `set`. Re-export only the - # approved keys into `GITHUB_ENV` so later steps inherit the same MSVC context. - $envLines = & cmd.exe /c ('"{0}" -no_logo -arch=x64 -host_arch=x64 >nul && set' -f $vsDevCmd) - foreach ($line in $envLines) { - if ($line -notmatch '^(.*?)=(.*)$') { - continue - } - - $name = $matches[1] - $value = $matches[2] - if ($varsToExport -contains $name) { - "$name=$value" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - } - } - - - name: Compute cache-stable Windows Bazel PATH - if: runner.os == 'Windows' - shell: pwsh - run: ./.github/scripts/compute-bazel-windows-path.ps1 - - - name: Enable Git long paths (Windows) - if: runner.os == 'Windows' - shell: pwsh - run: git config --global core.longpaths true diff --git a/reference/openai-codex/.github/actions/setup-msvc-env/action.yml b/reference/openai-codex/.github/actions/setup-msvc-env/action.yml deleted file mode 100644 index 287cb7f..0000000 --- a/reference/openai-codex/.github/actions/setup-msvc-env/action.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: setup-msvc-env -description: Expose an MSVC developer environment for the requested Windows target. -inputs: - target: - description: Rust target triple that will be built on this Windows runner. - required: true - host-arch: - description: Optional Visual Studio host architecture override. - required: false - default: "" - -runs: - using: composite - steps: - - name: Expose MSVC SDK environment - shell: pwsh - run: '& "$env:GITHUB_ACTION_PATH/setup-msvc-env.ps1" -Target "${{ inputs.target }}" -HostArch "${{ inputs.host-arch }}"' diff --git a/reference/openai-codex/.github/actions/setup-msvc-env/setup-msvc-env.ps1 b/reference/openai-codex/.github/actions/setup-msvc-env/setup-msvc-env.ps1 deleted file mode 100644 index e2706d9..0000000 --- a/reference/openai-codex/.github/actions/setup-msvc-env/setup-msvc-env.ps1 +++ /dev/null @@ -1,257 +0,0 @@ -param( - [Parameter(Mandatory = $true)] - [string]$Target, - - [string]$HostArch = "" -) - -# Cargo can cross-compile the Rust code for Windows ARM64 on a Windows x64 -# runner, but rustup alone does not expose the matching MSVC/UCRT include and -# library paths. Ask Visual Studio for the target-specific developer -# environment, then persist the relevant variables through GITHUB_ENV so the -# later Cargo step sees the same environment as a normal VsDevCmd shell. -switch ($Target) { - "x86_64-pc-windows-msvc" { - $TargetArch = "x64" - $RequiredComponent = "Microsoft.VisualStudio.Component.VC.Tools.x86.x64" - } - "aarch64-pc-windows-msvc" { - $TargetArch = "arm64" - $RequiredComponent = "Microsoft.VisualStudio.Component.VC.Tools.ARM64" - } - default { - throw "Unsupported Windows MSVC target: $Target" - } -} - -# VsDevCmd needs both sides of the cross compile: the architecture of the -# machine running the tools and the architecture of the binaries being linked. -# Infer the host from the runner unless a caller needs to override it. -if (-not $HostArch) { - $HostArch = if ($env:PROCESSOR_ARCHITEW6432 -eq "ARM64" -or $env:PROCESSOR_ARCHITECTURE -eq "ARM64") { - "arm64" - } else { - "x64" - } -} - -$VsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -if (-not (Test-Path $VsWhere)) { - throw "vswhere.exe not found" -} - -# Require the target VC tools component, not merely any Visual Studio install, -# so an x64 archive producer cannot silently link ARM64 tests with the wrong -# SDK/toolchain layout. -$InstallPath = & $VsWhere -latest -products * -requires $RequiredComponent -property installationPath 2>$null -if (-not $InstallPath) { - throw "Could not locate a Visual Studio installation with component $RequiredComponent" -} - -$VsDevCmd = Join-Path $InstallPath "Common7\Tools\VsDevCmd.bat" -if (-not (Test-Path $VsDevCmd)) { - throw "VsDevCmd.bat not found at $VsDevCmd" -} - -$VarsToExport = @( - "INCLUDE", - "LIB", - "LIBPATH", - "PATH", - "UCRTVersion", - "UniversalCRTSdkDir", - "VCINSTALLDIR", - "VCToolsInstallDir", - "WindowsLibPath", - "WindowsSdkBinPath", - "WindowsSdkDir", - "WindowsSDKLibVersion", - "WindowsSDKVersion" -) - -# Run VsDevCmd inside cmd.exe because it is a batch file, then copy just the -# variables Cargo/rustc need into the GitHub Actions environment file. PowerShell -# cannot mutate the parent composite-action environment directly. -$EnvLines = & cmd.exe /c ('"{0}" -no_logo -arch={1} -host_arch={2} >nul && set' -f $VsDevCmd, $TargetArch, $HostArch) -$VcToolsInstallDir = $null -foreach ($Line in $EnvLines) { - if ($Line -notmatch "^(.*?)=(.*)$") { - continue - } - - $Name = $Matches[1] - $Value = $Matches[2] - if ($VarsToExport -contains $Name) { - if ($Name -ieq "Path") { - $Name = "PATH" - } - if ($Name -eq "VCToolsInstallDir") { - $VcToolsInstallDir = $Value - } - "$Name=$Value" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - } -} - -if (-not $VcToolsInstallDir) { - throw "VCToolsInstallDir was not exported by VsDevCmd.bat" -} - -# Prefer Rust's bundled linker when rustup provides one, then Visual Studio's -# LLVM linker, and finally MSVC link.exe. This keeps the cross-compile path close -# to Rust's normal Windows MSVC behavior while still working on runner images -# where one of those linkers is absent. -$Linker = $null -$Rustc = Get-Command rustc -ErrorAction SilentlyContinue -if ($Rustc) { - $Sysroot = (& rustc --print sysroot 2>$null).Trim() - $RustHost = & rustc -vV 2>$null | Select-String "^host: " | ForEach-Object { $_.Line.Substring(6) } - if ($RustHost) { - $RustHost = $RustHost.Trim() - } - if ($Sysroot -and $RustHost) { - $RustLld = Join-Path $Sysroot "lib\rustlib\$RustHost\bin\rust-lld.exe" - if (Test-Path $RustLld) { - $Linker = $RustLld - } - } -} -if (-not $Linker) { - $Linker = Join-Path $InstallPath "VC\Tools\Llvm\x64\bin\lld-link.exe" -} -if (-not (Test-Path $Linker)) { - $Linker = Join-Path $VcToolsInstallDir "bin\Host${HostArch}\${TargetArch}\link.exe" -} -if (-not (Test-Path $Linker)) { - throw "Windows linker not found at $Linker" -} - -# rustc passes `/arm64hazardfree` for ARM64 MSVC links. The lld variants on our -# Windows x64 archive producers reject that flag, including when rustc places it -# inside a response file. Compile a tiny forwarding wrapper that strips only -# that unsupported flag, then delegate every other argument to the real linker. -if ($TargetArch -eq "arm64" -and (Split-Path -Leaf $Linker) -match "lld") { - $WrapperDir = Join-Path $env:RUNNER_TEMP "msvc-lld-wrapper" - New-Item -Path $WrapperDir -ItemType Directory -Force | Out-Null - $WrapperPath = Join-Path $WrapperDir "lld-link-wrapper.exe" - $WrapperSource = @' -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; - -internal static class Program -{ - private static int Main(string[] args) - { - var linker = Environment.GetEnvironmentVariable("MSVC_REAL_LINKER"); - if (string.IsNullOrEmpty(linker)) - { - Console.Error.WriteLine("MSVC_REAL_LINKER is not set"); - return 1; - } - - var startInfo = new ProcessStartInfo(linker) - { - UseShellExecute = false, - }; - var filteredArgs = new List { "-flavor", "link", "/defaultlib:ucrt", "/nodefaultlib:libucrt" }; - foreach (var arg in args) - { - if (!string.Equals(arg, "/arm64hazardfree", StringComparison.OrdinalIgnoreCase)) - { - filteredArgs.Add(QuoteArgument(FilterResponseFile(arg))); - } - } - startInfo.Arguments = string.Join(" ", filteredArgs); - - using var process = Process.Start(startInfo); - if (process is null) - { - Console.Error.WriteLine($"Failed to start linker: {linker}"); - return 1; - } - - process.WaitForExit(); - return process.ExitCode; - } - - private static string FilterResponseFile(string argument) - { - if (argument.Length < 2 || argument[0] != '@') - { - return argument; - } - - var responsePath = argument.Substring(1); - if (!File.Exists(responsePath)) - { - return argument; - } - - var filteredResponsePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".rsp"); - var responseContents = Regex.Replace( - File.ReadAllText(responsePath), - "/arm64hazardfree", - string.Empty, - RegexOptions.IgnoreCase); - File.WriteAllText(filteredResponsePath, responseContents); - return "@" + filteredResponsePath; - } - - private static string QuoteArgument(string argument) - { - if (argument.Length == 0) - { - return "\"\""; - } - if (argument.IndexOfAny(new[] { ' ', '\t', '"' }) < 0) - { - return argument; - } - - var quoted = new StringBuilder("\""); - var backslashes = 0; - foreach (var character in argument) - { - if (character == '\\') - { - backslashes++; - continue; - } - if (character == '"') - { - quoted.Append('\\', (backslashes * 2) + 1); - quoted.Append(character); - backslashes = 0; - continue; - } - - quoted.Append('\\', backslashes); - backslashes = 0; - quoted.Append(character); - } - quoted.Append('\\', backslashes * 2); - quoted.Append('"'); - return quoted.ToString(); - } -} -'@ - $WrapperSourcePath = Join-Path $WrapperDir "lld-link-wrapper.cs" - $WrapperSource | Out-File -FilePath $WrapperSourcePath -Encoding utf8 - $Csc = Join-Path $InstallPath "MSBuild\Current\Bin\Roslyn\csc.exe" - if (-not (Test-Path $Csc)) { - throw "csc.exe not found at $Csc" - } - & $Csc /nologo /target:exe /out:$WrapperPath $WrapperSourcePath - if ($LASTEXITCODE -ne 0) { - throw "Failed to compile lld-link wrapper" - } - "MSVC_REAL_LINKER=$Linker" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - $Linker = $WrapperPath -} - -Write-Output "Using Windows linker: $Linker" -$CargoTarget = $Target.ToUpperInvariant().Replace("-", "_") -"CARGO_TARGET_${CargoTarget}_LINKER=$Linker" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append diff --git a/reference/openai-codex/.github/actions/setup-rusty-v8/action.yml b/reference/openai-codex/.github/actions/setup-rusty-v8/action.yml deleted file mode 100644 index d9c4484..0000000 --- a/reference/openai-codex/.github/actions/setup-rusty-v8/action.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: setup-rusty-v8 -description: Download and verify Codex-built rusty_v8 artifacts for Cargo builds. -inputs: - target: - description: Rust target triple with Codex-built V8 release artifacts. - required: true - -runs: - using: composite - steps: - - name: Configure rusty_v8 artifact overrides and verify checksums - shell: bash - env: - TARGET: ${{ inputs.target }} - run: | - set -euo pipefail - - version="$(python3 "${GITHUB_WORKSPACE}/.github/scripts/rusty_v8_bazel.py" resolved-v8-crate-version)" - release_tag="rusty-v8-v${version}" - base_url="https://github.com/openai/codex/releases/download/${release_tag}" - binding_dir="${RUNNER_TEMP}/rusty_v8" - archive_path="${binding_dir}/librusty_v8_release_${TARGET}.a.gz" - binding_path="${binding_dir}/src_binding_release_${TARGET}.rs" - checksums_path="${binding_dir}/rusty_v8_release_${TARGET}.sha256" - - mkdir -p "${binding_dir}" - curl -fsSL "${base_url}/librusty_v8_release_${TARGET}.a.gz" -o "${archive_path}" - curl -fsSL "${base_url}/src_binding_release_${TARGET}.rs" -o "${binding_path}" - curl -fsSL "${base_url}/rusty_v8_release_${TARGET}.sha256" -o "${checksums_path}" - - if [[ "$(wc -l < "${checksums_path}")" -ne 2 ]]; then - echo "Expected exactly two checksums for ${TARGET} in ${checksums_path}" >&2 - exit 1 - fi - - if command -v sha256sum >/dev/null 2>&1; then - (cd "${binding_dir}" && sha256sum -c "${checksums_path}") - else - (cd "${binding_dir}" && shasum -a 256 -c "${checksums_path}") - fi - echo "RUSTY_V8_ARCHIVE=${archive_path}" >> "${GITHUB_ENV}" - echo "RUSTY_V8_SRC_BINDING_PATH=${binding_path}" >> "${GITHUB_ENV}" diff --git a/reference/openai-codex/.github/actions/windows-code-sign/action.yml b/reference/openai-codex/.github/actions/windows-code-sign/action.yml deleted file mode 100644 index 634d647..0000000 --- a/reference/openai-codex/.github/actions/windows-code-sign/action.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: windows-code-sign -description: Sign Windows binaries with Azure Trusted Signing. -inputs: - target: - description: Target triple for the artifacts to sign. - required: true - binaries: - description: Space-delimited binary basenames to sign. - default: "codex codex-responses-api-proxy codex-windows-sandbox-setup codex-command-runner" - client-id: - description: Azure Trusted Signing client ID. - required: true - tenant-id: - description: Azure tenant ID for Trusted Signing. - required: true - subscription-id: - description: Azure subscription ID for Trusted Signing. - required: true - endpoint: - description: Azure Trusted Signing endpoint. - required: true - account-name: - description: Azure Trusted Signing account name. - required: true - certificate-profile-name: - description: Certificate profile name for signing. - required: true - -runs: - using: composite - steps: - - name: Azure login for Trusted Signing (OIDC) - uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0 - with: - client-id: ${{ inputs.client-id }} - tenant-id: ${{ inputs.tenant-id }} - subscription-id: ${{ inputs.subscription-id }} - - - name: Prepare file list - id: prepare - shell: bash - env: - TARGET: ${{ inputs.target }} - BINARIES: ${{ inputs.binaries }} - run: | - set -euo pipefail - - { - echo "files<> "$GITHUB_OUTPUT" - - - name: Sign Windows binaries with Azure Trusted Signing - uses: azure/trusted-signing-action@1d365fec12862c4aa68fcac418143d73f0cea293 # v0.5.11 - with: - endpoint: ${{ inputs.endpoint }} - trusted-signing-account-name: ${{ inputs.account-name }} - certificate-profile-name: ${{ inputs.certificate-profile-name }} - exclude-environment-credential: true - exclude-workload-identity-credential: true - exclude-managed-identity-credential: true - exclude-shared-token-cache-credential: true - exclude-visual-studio-credential: true - exclude-visual-studio-code-credential: true - exclude-azure-cli-credential: false - exclude-azure-powershell-credential: true - exclude-azure-developer-cli-credential: true - exclude-interactive-browser-credential: true - cache-dependencies: false - files: ${{ steps.prepare.outputs.files }} diff --git a/reference/openai-codex/.github/blob-size-allowlist.txt b/reference/openai-codex/.github/blob-size-allowlist.txt deleted file mode 100644 index 9375b49..0000000 --- a/reference/openai-codex/.github/blob-size-allowlist.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Paths are matched exactly, relative to the repository root. -# Keep this list short and limited to intentional large checked-in assets. - -.github/codex-cli-splash.png -MODULE.bazel.lock -codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json -codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json -codex-rs/tui/tests/fixtures/oss-story.jsonl -codex-rs/tui_app_server/tests/fixtures/oss-story.jsonl -codex-rs/tui/src/app.rs diff --git a/reference/openai-codex/.github/codex-cli-splash.png b/reference/openai-codex/.github/codex-cli-splash.png deleted file mode 100644 index d0f50e5..0000000 Binary files a/reference/openai-codex/.github/codex-cli-splash.png and /dev/null differ diff --git a/reference/openai-codex/.github/codex/home/config.toml b/reference/openai-codex/.github/codex/home/config.toml deleted file mode 100644 index 7b06521..0000000 --- a/reference/openai-codex/.github/codex/home/config.toml +++ /dev/null @@ -1,3 +0,0 @@ -model = "gpt-5.1" - -# Consider setting [mcp_servers] here! diff --git a/reference/openai-codex/.github/codex/labels/codex-attempt.md b/reference/openai-codex/.github/codex/labels/codex-attempt.md deleted file mode 100644 index b2a3e93..0000000 --- a/reference/openai-codex/.github/codex/labels/codex-attempt.md +++ /dev/null @@ -1,9 +0,0 @@ -Attempt to solve the reported issue. - -If a code change is required, create a new branch, commit the fix, and open a pull request that resolves the problem. - -Here is the original GitHub issue that triggered this run: - -### {CODEX_ACTION_ISSUE_TITLE} - -{CODEX_ACTION_ISSUE_BODY} diff --git a/reference/openai-codex/.github/codex/labels/codex-review.md b/reference/openai-codex/.github/codex/labels/codex-review.md deleted file mode 100644 index 7c6c14a..0000000 --- a/reference/openai-codex/.github/codex/labels/codex-review.md +++ /dev/null @@ -1,7 +0,0 @@ -Review this PR and respond with a very concise final message, formatted in Markdown. - -There should be a summary of the changes (1-2 sentences) and a few bullet points if necessary. - -Then provide the **review** (1-2 sentences plus bullet points, friendly tone). - -{CODEX_ACTION_GITHUB_EVENT_PATH} contains the JSON that triggered this GitHub workflow. It contains the `base` and `head` refs that define this PR. Both refs are available locally. diff --git a/reference/openai-codex/.github/codex/labels/codex-rust-review.md b/reference/openai-codex/.github/codex/labels/codex-rust-review.md deleted file mode 100644 index ae74953..0000000 --- a/reference/openai-codex/.github/codex/labels/codex-rust-review.md +++ /dev/null @@ -1,139 +0,0 @@ -Review this PR and respond with a very concise final message, formatted in Markdown. - -There should be a summary of the changes (1-2 sentences) and a few bullet points if necessary. - -Then provide the **review** (1-2 sentences plus bullet points, friendly tone). - -Things to look out for when doing the review: - -## General Principles - -- **Make sure the pull request body explains the motivation behind the change.** If the author has failed to do this, call it out, and if you think you can deduce the motivation behind the change, propose copy. -- Ideally, the PR body also contains a small summary of the change. For small changes, the PR title may be sufficient. -- Each PR should ideally do one conceptual thing. For example, if a PR does a refactoring as well as introducing a new feature, push back and suggest the refactoring be done in a separate PR. This makes things easier for the reviewer, as refactoring changes can often be far-reaching, yet quick to review. -- When introducing new code, be on the lookout for code that duplicates existing code. When found, propose a way to refactor the existing code such that it should be reused. - -## Code Organization - -- Each crate in the Cargo workspace in `codex-rs` has a specific purpose: make a note if you believe new code is not introduced in the correct crate. -- When possible, try to keep the `core` crate as small as possible. Non-core but shared logic is often a good candidate for `codex-rs/common`. -- Be wary of large files and offer suggestions for how to break things into more reasonably-sized files. -- Rust files should generally be organized such that the public parts of the API appear near the top of the file and helper functions go below. This is analogous to the "inverted pyramid" structure that is favored in journalism. - -## Assertions in Tests - -Assert the equality of the entire objects instead of doing "piecemeal comparisons," performing `assert_eq!()` on individual fields. - -Note that unit tests also function as "executable documentation." As shown in the following example, "piecemeal comparisons" are often more verbose, provide less coverage, and are not as useful as executable documentation. - -For example, suppose you have the following enum: - -```rust -#[derive(Debug, PartialEq)] -enum Message { - Request { - id: String, - method: String, - params: Option, - }, - Notification { - method: String, - params: Option, - }, -} -``` - -This is an example of a _piecemeal_ comparison: - -```rust -// BAD: Piecemeal Comparison - -#[test] -fn test_get_latest_messages() { - let messages = get_latest_messages(); - assert_eq!(messages.len(), 2); - - let m0 = &messages[0]; - match m0 { - Message::Request { id, method, params } => { - assert_eq!(id, "123"); - assert_eq!(method, "subscribe"); - assert_eq!( - *params, - Some(json!({ - "conversation_id": "x42z86" - })) - ) - } - Message::Notification { .. } => { - panic!("expected Request"); - } - } - - let m1 = &messages[1]; - match m1 { - Message::Request { .. } => { - panic!("expected Notification"); - } - Message::Notification { method, params } => { - assert_eq!(method, "log"); - assert_eq!( - *params, - Some(json!({ - "level": "info", - "message": "subscribed" - })) - ) - } - } -} -``` - -This is a _deep_ comparison: - -```rust -// GOOD: Verify the entire structure with a single assert_eq!(). - -use pretty_assertions::assert_eq; - -#[test] -fn test_get_latest_messages() { - let messages = get_latest_messages(); - - assert_eq!( - vec![ - Message::Request { - id: "123".to_string(), - method: "subscribe".to_string(), - params: Some(json!({ - "conversation_id": "x42z86" - })), - }, - Message::Notification { - method: "log".to_string(), - params: Some(json!({ - "level": "info", - "message": "subscribed" - })), - }, - ], - messages, - ); -} -``` - -## More Tactical Rust Things To Look Out For - -- Do not use `unsafe` (unless you have a really, really good reason like using an operating system API directly and no safe wrapper exists). For example, there are cases where it is tempting to use `unsafe` in order to use `std::env::set_var()`, but this indeed `unsafe` and has led to race conditions on multiple occasions. (When this happens, find a mechanism other than environment variables to use for configuration.) -- Encourage the use of small enums or the newtype pattern in Rust if it helps readability without adding significant cognitive load or lines of code. -- If you see opportunities for the changes in a diff to use more idiomatic Rust, please make specific recommendations. For example, favor the use of expressions over `return`. -- When modifying a `Cargo.toml` file, make sure that dependency lists stay alphabetically sorted. Also consider whether a new dependency is added to the appropriate place (e.g., `[dependencies]` versus `[dev-dependencies]`) - -## Pull Request Body - -- If the nature of the change seems to have a visual component (which is often the case for changes to `codex-rs/tui`), recommend including a screenshot or video to demonstrate the change, if appropriate. -- References to existing GitHub issues and PRs are encouraged, where appropriate, though you likely do not have network access, so may not be able to help here. - -# PR Information - -{CODEX_ACTION_GITHUB_EVENT_PATH} contains the JSON that triggered this GitHub workflow. It contains the `base` and `head` refs that define this PR. Both refs are available locally. diff --git a/reference/openai-codex/.github/codex/labels/codex-triage.md b/reference/openai-codex/.github/codex/labels/codex-triage.md deleted file mode 100644 index 46ed362..0000000 --- a/reference/openai-codex/.github/codex/labels/codex-triage.md +++ /dev/null @@ -1,7 +0,0 @@ -Troubleshoot whether the reported issue is valid. - -Provide a concise and respectful comment summarizing the findings. - -### {CODEX_ACTION_ISSUE_TITLE} - -{CODEX_ACTION_ISSUE_BODY} diff --git a/reference/openai-codex/.github/dependabot.yaml b/reference/openai-codex/.github/dependabot.yaml deleted file mode 100644 index 9eeb8f1..0000000 --- a/reference/openai-codex/.github/dependabot.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference#package-ecosystem- - -version: 2 -updates: - - package-ecosystem: bun - directory: .github/actions/codex - schedule: - interval: weekly - cooldown: - default-days: 7 - - package-ecosystem: cargo - directories: - - codex-rs - - codex-rs/* - schedule: - interval: weekly - cooldown: - default-days: 7 - - package-ecosystem: devcontainers - directory: / - schedule: - interval: weekly - cooldown: - default-days: 7 - - package-ecosystem: docker - directory: codex-cli - schedule: - interval: weekly - cooldown: - default-days: 7 - - package-ecosystem: github-actions - directory: / - schedule: - interval: weekly - cooldown: - default-days: 7 - - package-ecosystem: rust-toolchain - directory: codex-rs - schedule: - interval: weekly - cooldown: - default-days: 7 diff --git a/reference/openai-codex/.github/dotslash-argument-comment-lint-config.json b/reference/openai-codex/.github/dotslash-argument-comment-lint-config.json deleted file mode 100644 index 19a2a48..0000000 --- a/reference/openai-codex/.github/dotslash-argument-comment-lint-config.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "outputs": { - "argument-comment-lint": { - "platforms": { - "macos-aarch64": { - "regex": "^argument-comment-lint-aarch64-apple-darwin\\.tar\\.gz$", - "path": "argument-comment-lint/bin/argument-comment-lint" - }, - "linux-x86_64": { - "regex": "^argument-comment-lint-x86_64-unknown-linux-gnu\\.tar\\.gz$", - "path": "argument-comment-lint/bin/argument-comment-lint" - }, - "linux-aarch64": { - "regex": "^argument-comment-lint-aarch64-unknown-linux-gnu\\.tar\\.gz$", - "path": "argument-comment-lint/bin/argument-comment-lint" - }, - "windows-x86_64": { - "regex": "^argument-comment-lint-x86_64-pc-windows-msvc\\.zip$", - "path": "argument-comment-lint/bin/argument-comment-lint.exe" - } - } - } - } -} diff --git a/reference/openai-codex/.github/dotslash-config.json b/reference/openai-codex/.github/dotslash-config.json deleted file mode 100644 index 78ea6b3..0000000 --- a/reference/openai-codex/.github/dotslash-config.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "outputs": { - "codex": { - "platforms": { - "macos-aarch64": { - "regex": "^codex-package-aarch64-apple-darwin\\.tar\\.zst$", - "path": "bin/codex" - }, - "macos-x86_64": { - "regex": "^codex-package-x86_64-apple-darwin\\.tar\\.zst$", - "path": "bin/codex" - }, - "linux-x86_64": { - "regex": "^codex-package-x86_64-unknown-linux-musl\\.tar\\.zst$", - "path": "bin/codex" - }, - "linux-aarch64": { - "regex": "^codex-package-aarch64-unknown-linux-musl\\.tar\\.zst$", - "path": "bin/codex" - }, - "windows-x86_64": { - "regex": "^codex-package-x86_64-pc-windows-msvc\\.tar\\.zst$", - "path": "bin/codex.exe" - }, - "windows-aarch64": { - "regex": "^codex-package-aarch64-pc-windows-msvc\\.tar\\.zst$", - "path": "bin/codex.exe" - } - } - }, - "codex-app-server": { - "platforms": { - "macos-aarch64": { - "regex": "^codex-app-server-package-aarch64-apple-darwin\\.tar\\.zst$", - "path": "bin/codex-app-server" - }, - "macos-x86_64": { - "regex": "^codex-app-server-package-x86_64-apple-darwin\\.tar\\.zst$", - "path": "bin/codex-app-server" - }, - "linux-x86_64": { - "regex": "^codex-app-server-package-x86_64-unknown-linux-musl\\.tar\\.zst$", - "path": "bin/codex-app-server" - }, - "linux-aarch64": { - "regex": "^codex-app-server-package-aarch64-unknown-linux-musl\\.tar\\.zst$", - "path": "bin/codex-app-server" - }, - "windows-x86_64": { - "regex": "^codex-app-server-package-x86_64-pc-windows-msvc\\.tar\\.zst$", - "path": "bin/codex-app-server.exe" - }, - "windows-aarch64": { - "regex": "^codex-app-server-package-aarch64-pc-windows-msvc\\.tar\\.zst$", - "path": "bin/codex-app-server.exe" - } - } - }, - "codex-responses-api-proxy": { - "platforms": { - "macos-aarch64": { - "regex": "^codex-responses-api-proxy-aarch64-apple-darwin\\.zst$", - "path": "codex-responses-api-proxy" - }, - "macos-x86_64": { - "regex": "^codex-responses-api-proxy-x86_64-apple-darwin\\.zst$", - "path": "codex-responses-api-proxy" - }, - "linux-x86_64": { - "regex": "^codex-responses-api-proxy-x86_64-unknown-linux-musl\\.zst$", - "path": "codex-responses-api-proxy" - }, - "linux-aarch64": { - "regex": "^codex-responses-api-proxy-aarch64-unknown-linux-musl\\.zst$", - "path": "codex-responses-api-proxy" - }, - "windows-x86_64": { - "regex": "^codex-responses-api-proxy-x86_64-pc-windows-msvc\\.exe\\.zst$", - "path": "codex-responses-api-proxy.exe" - }, - "windows-aarch64": { - "regex": "^codex-responses-api-proxy-aarch64-pc-windows-msvc\\.exe\\.zst$", - "path": "codex-responses-api-proxy.exe" - } - } - }, - "bwrap": { - "platforms": { - "linux-x86_64": { - "regex": "^bwrap-x86_64-unknown-linux-musl\\.zst$", - "path": "bwrap" - }, - "linux-aarch64": { - "regex": "^bwrap-aarch64-unknown-linux-musl\\.zst$", - "path": "bwrap" - } - } - }, - "codex-command-runner": { - "platforms": { - "windows-x86_64": { - "regex": "^codex-command-runner-x86_64-pc-windows-msvc\\.exe\\.zst$", - "path": "codex-command-runner.exe" - }, - "windows-aarch64": { - "regex": "^codex-command-runner-aarch64-pc-windows-msvc\\.exe\\.zst$", - "path": "codex-command-runner.exe" - } - } - }, - "codex-windows-sandbox-setup": { - "platforms": { - "windows-x86_64": { - "regex": "^codex-windows-sandbox-setup-x86_64-pc-windows-msvc\\.exe\\.zst$", - "path": "codex-windows-sandbox-setup.exe" - }, - "windows-aarch64": { - "regex": "^codex-windows-sandbox-setup-aarch64-pc-windows-msvc\\.exe\\.zst$", - "path": "codex-windows-sandbox-setup.exe" - } - } - } - } -} diff --git a/reference/openai-codex/.github/dotslash-zsh-config.json b/reference/openai-codex/.github/dotslash-zsh-config.json deleted file mode 100644 index 37285f1..0000000 --- a/reference/openai-codex/.github/dotslash-zsh-config.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "outputs": { - "codex-zsh": { - "platforms": { - "macos-aarch64": { - "name": "codex-zsh-aarch64-apple-darwin.tar.gz", - "format": "tar.gz", - "path": "codex-zsh/bin/zsh" - }, - "macos-x86_64": { - "name": "codex-zsh-x86_64-apple-darwin.tar.gz", - "format": "tar.gz", - "path": "codex-zsh/bin/zsh" - }, - "linux-x86_64": { - "name": "codex-zsh-x86_64-unknown-linux-musl.tar.gz", - "format": "tar.gz", - "path": "codex-zsh/bin/zsh" - }, - "linux-aarch64": { - "name": "codex-zsh-aarch64-unknown-linux-musl.tar.gz", - "format": "tar.gz", - "path": "codex-zsh/bin/zsh" - } - } - } - } -} diff --git a/reference/openai-codex/.github/pull_request_template.md b/reference/openai-codex/.github/pull_request_template.md deleted file mode 100644 index 4ea424c..0000000 --- a/reference/openai-codex/.github/pull_request_template.md +++ /dev/null @@ -1,8 +0,0 @@ -# External (non-OpenAI) Pull Request Requirements - -External code contributions are by invitation only. Please read the dedicated "Contributing" markdown file for details: -https://github.com/openai/codex/blob/main/docs/contributing.md - -If your PR conforms to our contribution guidelines, replace this text with a detailed and high quality description of your changes. - -Include a link to a bug report or enhancement request. diff --git a/reference/openai-codex/.github/scripts/build-codex-package-archive.sh b/reference/openai-codex/.github/scripts/build-codex-package-archive.sh deleted file mode 100644 index 80da4cf..0000000 --- a/reference/openai-codex/.github/scripts/build-codex-package-archive.sh +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -usage() { - cat <<'EOF' -Usage: build-codex-package-archive.sh \ - --target \ - --bundle \ - --entrypoint-dir \ - --archive-dir \ - [--bwrap-bin ] \ - [--codex-command-runner-bin ] \ - [--codex-windows-sandbox-setup-bin ] \ - [--target-suffixed-entrypoint] -EOF -} - -target="" -bundle="" -entrypoint_dir="" -archive_dir="" -target_suffixed_entrypoint="false" -resource_args=() -bwrap_bin_provided="false" -command_runner_bin_provided="false" -sandbox_setup_bin_provided="false" - -while [[ $# -gt 0 ]]; do - case "$1" in - --target) - target="${2:?--target requires a value}" - shift 2 - ;; - --bundle) - bundle="${2:?--bundle requires a value}" - shift 2 - ;; - --entrypoint-dir) - entrypoint_dir="${2:?--entrypoint-dir requires a value}" - shift 2 - ;; - --archive-dir) - archive_dir="${2:?--archive-dir requires a value}" - shift 2 - ;; - --bwrap-bin) - resource_args+=(--bwrap-bin "${2:?--bwrap-bin requires a value}") - bwrap_bin_provided="true" - shift 2 - ;; - --codex-command-runner-bin) - resource_args+=( - --codex-command-runner-bin - "${2:?--codex-command-runner-bin requires a value}" - ) - command_runner_bin_provided="true" - shift 2 - ;; - --codex-windows-sandbox-setup-bin) - resource_args+=( - --codex-windows-sandbox-setup-bin - "${2:?--codex-windows-sandbox-setup-bin requires a value}" - ) - sandbox_setup_bin_provided="true" - shift 2 - ;; - --target-suffixed-entrypoint) - target_suffixed_entrypoint="true" - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - echo "Unexpected argument: $1" >&2 - usage >&2 - exit 1 - ;; - esac -done - -if [[ -z "$target" || -z "$bundle" || -z "$entrypoint_dir" || -z "$archive_dir" ]]; then - usage >&2 - exit 1 -fi - -case "$bundle" in - primary) - variant="codex" - entrypoint="codex" - archive_stem="codex-package" - ;; - app-server) - variant="codex-app-server" - entrypoint="codex-app-server" - archive_stem="codex-app-server-package" - ;; - *) - echo "No Codex package variant for bundle: $bundle" >&2 - exit 1 - ;; -esac - -exe_suffix="" -case "$target" in - *windows*) - exe_suffix=".exe" - ;; -esac - -entrypoint_name="$entrypoint" -if [[ "$target_suffixed_entrypoint" == "true" ]]; then - entrypoint_name="${entrypoint_name}-${target}" -fi - -case "$target" in - *linux*) - bwrap_bin="${entrypoint_dir%/}/bwrap" - if [[ "$bwrap_bin_provided" == "false" && -f "$bwrap_bin" ]]; then - resource_args+=(--bwrap-bin "$bwrap_bin") - fi - ;; - *windows*) - command_runner_bin="${entrypoint_dir%/}/codex-command-runner.exe" - sandbox_setup_bin="${entrypoint_dir%/}/codex-windows-sandbox-setup.exe" - if [[ "$command_runner_bin_provided" == "false" && -f "$command_runner_bin" ]]; then - resource_args+=(--codex-command-runner-bin "$command_runner_bin") - fi - if [[ "$sandbox_setup_bin_provided" == "false" && -f "$sandbox_setup_bin" ]]; then - resource_args+=(--codex-windows-sandbox-setup-bin "$sandbox_setup_bin") - fi - ;; -esac - -repo_root="${GITHUB_WORKSPACE:-}" -if [[ -z "$repo_root" ]]; then - repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -fi - -if command -v python3 >/dev/null 2>&1; then - python_bin="python3" -else - python_bin="python" -fi - -if ! command -v zstd >/dev/null 2>&1 && [[ -x "${repo_root}/.github/workflows/zstd" ]]; then - export PATH="${repo_root}/.github/workflows:${PATH}" -fi - -mkdir -p "$archive_dir" -package_dir="${RUNNER_TEMP:-/tmp}/${archive_stem}-${target}" -gzip_archive_path="${archive_dir}/${archive_stem}-${target}.tar.gz" -zstd_archive_path="${archive_dir}/${archive_stem}-${target}.tar.zst" -rm -rf "$package_dir" - -python_args=( - "${repo_root}/scripts/build_codex_package.py" - --target "$target" - --variant "$variant" - --entrypoint-bin "${entrypoint_dir%/}/${entrypoint_name}${exe_suffix}" - --cargo-profile release - --package-dir "$package_dir" - --archive-output "$gzip_archive_path" - --archive-output "$zstd_archive_path" -) -if ((${#resource_args[@]} > 0)); then - python_args+=("${resource_args[@]}") -fi -python_args+=(--force) - -"$python_bin" "${python_args[@]}" diff --git a/reference/openai-codex/.github/scripts/build-zsh-release-artifact.sh b/reference/openai-codex/.github/scripts/build-zsh-release-artifact.sh deleted file mode 100755 index 4fc3db3..0000000 --- a/reference/openai-codex/.github/scripts/build-zsh-release-artifact.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -if [[ "$#" -ne 1 ]]; then - echo "usage: $0 " >&2 - exit 1 -fi - -archive_path="$1" -workspace="${GITHUB_WORKSPACE:?missing GITHUB_WORKSPACE}" -zsh_commit="${ZSH_COMMIT:?missing ZSH_COMMIT}" -zsh_patch="${ZSH_PATCH:?missing ZSH_PATCH}" -temp_root="${RUNNER_TEMP:-/tmp}" -work_root="$(mktemp -d "${temp_root%/}/codex-zsh-release.XXXXXX")" -trap 'rm -rf "$work_root"' EXIT - -source_root="${work_root}/zsh" -package_root="${work_root}/codex-zsh" -wrapper_path="${work_root}/exec-wrapper" -stdout_path="${work_root}/stdout.txt" -wrapper_log_path="${work_root}/wrapper.log" - -git clone https://git.code.sf.net/p/zsh/code "$source_root" -cd "$source_root" -git checkout "$zsh_commit" -git apply "${workspace}/${zsh_patch}" -./Util/preconfig -./configure - -cores="$(command -v nproc >/dev/null 2>&1 && nproc || getconf _NPROCESSORS_ONLN)" -make -j"${cores}" - -cat > "$wrapper_path" <<'EOF' -#!/usr/bin/env bash -set -euo pipefail -: "${CODEX_WRAPPER_LOG:?missing CODEX_WRAPPER_LOG}" -printf '%s\n' "$@" > "$CODEX_WRAPPER_LOG" -file="$1" -shift -if [[ "$#" -eq 0 ]]; then - exec "$file" -fi -arg0="$1" -shift -exec -a "$arg0" "$file" "$@" -EOF -chmod +x "$wrapper_path" - -CODEX_WRAPPER_LOG="$wrapper_log_path" \ -EXEC_WRAPPER="$wrapper_path" \ -"${source_root}/Src/zsh" -fc '/bin/echo smoke-zsh' > "$stdout_path" - -grep -Fx "smoke-zsh" "$stdout_path" -grep -Fx "/bin/echo" "$wrapper_log_path" - -mkdir -p "$package_root/bin" "$(dirname "${workspace}/${archive_path}")" -cp "${source_root}/Src/zsh" "$package_root/bin/zsh" -chmod +x "$package_root/bin/zsh" - -(cd "$work_root" && tar -czf "${workspace}/${archive_path}" codex-zsh) diff --git a/reference/openai-codex/.github/scripts/compute-bazel-windows-path.ps1 b/reference/openai-codex/.github/scripts/compute-bazel-windows-path.ps1 deleted file mode 100644 index 81fd668..0000000 --- a/reference/openai-codex/.github/scripts/compute-bazel-windows-path.ps1 +++ /dev/null @@ -1,113 +0,0 @@ -<# -BuildBuddy cache keys include the action and test environment, so Bazel should -not inherit the full hosted-runner PATH on Windows. That PATH includes volatile -tool entries, such as Maven, that can change independently of this repo and -cause avoidable cache misses. - -This script derives a smaller, cache-stable PATH that keeps the Windows -toolchain entries Bazel-backed CI tasks need: MSVC and Windows SDK paths, -MinGW runtime DLL paths for gnullvm-built tests, Git, PowerShell, Node, Python, -DotSlash, and the standard Windows system directories. -`setup-bazel-ci` runs this after exporting the MSVC environment, and the script -publishes the result via `GITHUB_ENV` as `CODEX_BAZEL_WINDOWS_PATH` so later -steps can pass that explicit PATH to Bazel. -#> - -$stablePathEntries = New-Object System.Collections.Generic.List[string] -$seenEntries = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) -$windowsAppsPath = if ([string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) { - $null -} else { - "$($env:LOCALAPPDATA)\Microsoft\WindowsApps" -} -$windowsDir = if ($env:WINDIR) { - $env:WINDIR -} elseif ($env:SystemRoot) { - $env:SystemRoot -} else { - $null -} - -function Add-StablePathEntry { - param([string]$PathEntry) - - if ([string]::IsNullOrWhiteSpace($PathEntry)) { - return - } - - if ($seenEntries.Add($PathEntry)) { - [void]$stablePathEntries.Add($PathEntry) - } -} - -foreach ($pathEntry in ($env:PATH -split ';')) { - if ([string]::IsNullOrWhiteSpace($pathEntry)) { - continue - } - - if ( - $pathEntry -like '*Microsoft Visual Studio*' -or - $pathEntry -like '*Windows Kits*' -or - $pathEntry -like '*Microsoft SDKs*' -or - $pathEntry -eq 'C:\mingw64\bin' -or - $pathEntry -like 'C:\msys64\*\bin' -or - $pathEntry -like 'C:\Program Files\Git\*' -or - $pathEntry -like 'C:\Program Files\PowerShell\*' -or - $pathEntry -like 'C:\hostedtoolcache\windows\node\*' -or - $pathEntry -like 'C:\hostedtoolcache\windows\Python\*' -or - $pathEntry -eq 'D:\a\_temp\install-dotslash\bin' -or - ($windowsDir -and ($pathEntry -eq $windowsDir -or $pathEntry -like "${windowsDir}\*")) - ) { - Add-StablePathEntry $pathEntry - } -} - -$gitCommand = Get-Command git -ErrorAction SilentlyContinue -if ($gitCommand) { - Add-StablePathEntry (Split-Path $gitCommand.Source -Parent) -} - -$nodeCommand = Get-Command node -ErrorAction SilentlyContinue -if ($nodeCommand) { - Add-StablePathEntry (Split-Path $nodeCommand.Source -Parent) -} - -$python3Command = Get-Command python3 -ErrorAction SilentlyContinue -if ($python3Command) { - Add-StablePathEntry (Split-Path $python3Command.Source -Parent) -} - -$pythonCommand = Get-Command python -ErrorAction SilentlyContinue -if ($pythonCommand) { - Add-StablePathEntry (Split-Path $pythonCommand.Source -Parent) -} - -$pwshCommand = Get-Command pwsh -ErrorAction SilentlyContinue -if ($pwshCommand) { - Add-StablePathEntry (Split-Path $pwshCommand.Source -Parent) -} - -foreach ($mingwPath in @('C:\mingw64\bin', 'C:\msys64\mingw64\bin', 'C:\msys64\ucrt64\bin')) { - if (Test-Path $mingwPath) { - Add-StablePathEntry $mingwPath - } -} - -if ($windowsAppsPath) { - Add-StablePathEntry $windowsAppsPath -} - -if ($stablePathEntries.Count -eq 0) { - throw 'Failed to derive cache-stable Windows PATH.' -} - -if ([string]::IsNullOrWhiteSpace($env:GITHUB_ENV)) { - throw 'GITHUB_ENV must be set.' -} - -$stablePath = $stablePathEntries -join ';' -Write-Host 'Derived CODEX_BAZEL_WINDOWS_PATH entries:' -foreach ($pathEntry in $stablePathEntries) { - Write-Host " $pathEntry" -} -"CODEX_BAZEL_WINDOWS_PATH=$stablePath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append diff --git a/reference/openai-codex/.github/scripts/install-musl-build-tools.sh b/reference/openai-codex/.github/scripts/install-musl-build-tools.sh deleted file mode 100644 index e4c6683..0000000 --- a/reference/openai-codex/.github/scripts/install-musl-build-tools.sh +++ /dev/null @@ -1,279 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -: "${TARGET:?TARGET environment variable is required}" -: "${GITHUB_ENV:?GITHUB_ENV environment variable is required}" - -apt_update_args=() -if [[ -n "${APT_UPDATE_ARGS:-}" ]]; then - # shellcheck disable=SC2206 - apt_update_args=(${APT_UPDATE_ARGS}) -fi - -apt_install_args=() -if [[ -n "${APT_INSTALL_ARGS:-}" ]]; then - # shellcheck disable=SC2206 - apt_install_args=(${APT_INSTALL_ARGS}) -fi - -sudo apt-get update "${apt_update_args[@]}" -sudo apt-get install -y "${apt_install_args[@]}" ca-certificates curl musl-tools pkg-config libcap-dev g++ clang libc++-dev libc++abi-dev lld xz-utils - -case "${TARGET}" in - x86_64-unknown-linux-musl) - arch="x86_64" - ;; - aarch64-unknown-linux-musl) - arch="aarch64" - ;; - *) - echo "Unexpected musl target: ${TARGET}" >&2 - exit 1 - ;; -esac - -libcap_version="2.75" -libcap_sha256="de4e7e064c9ba451d5234dd46e897d7c71c96a9ebf9a0c445bc04f4742d83632" -libcap_tarball_name="libcap-${libcap_version}.tar.xz" -libcap_download_url="https://mirrors.edge.kernel.org/pub/linux/libs/security/linux-privs/libcap2/${libcap_tarball_name}" - -# Use the musl toolchain as the Rust linker to avoid Zig injecting its own CRT. -if command -v "${arch}-linux-musl-gcc" >/dev/null; then - musl_linker="$(command -v "${arch}-linux-musl-gcc")" -elif command -v musl-gcc >/dev/null; then - musl_linker="$(command -v musl-gcc)" -else - echo "musl gcc not found after install; arch=${arch}" >&2 - exit 1 -fi - -zig_target="${TARGET/-unknown-linux-musl/-linux-musl}" -runner_temp="${RUNNER_TEMP:-/tmp}" -tool_root="${runner_temp}/codex-musl-tools-${TARGET}" -mkdir -p "${tool_root}" - -libcap_root="${tool_root}/libcap-${libcap_version}" -libcap_src_root="${libcap_root}/src" -libcap_prefix="${libcap_root}/prefix" -libcap_pkgconfig_dir="${libcap_prefix}/lib/pkgconfig" - -if [[ ! -f "${libcap_prefix}/lib/libcap.a" ]]; then - mkdir -p "${libcap_src_root}" "${libcap_prefix}/lib" "${libcap_prefix}/include/sys" "${libcap_prefix}/include/linux" "${libcap_pkgconfig_dir}" - libcap_tarball="${libcap_root}/${libcap_tarball_name}" - - curl -fsSL "${libcap_download_url}" -o "${libcap_tarball}" - echo "${libcap_sha256} ${libcap_tarball}" | sha256sum -c - - - tar -xJf "${libcap_tarball}" -C "${libcap_src_root}" - libcap_source_dir="${libcap_src_root}/libcap-${libcap_version}" - make -C "${libcap_source_dir}/libcap" -j"$(nproc)" \ - CC="${musl_linker}" \ - AR=ar \ - RANLIB=ranlib - - cp "${libcap_source_dir}/libcap/libcap.a" "${libcap_prefix}/lib/libcap.a" - cp "${libcap_source_dir}/libcap/include/uapi/linux/capability.h" "${libcap_prefix}/include/linux/capability.h" - cp "${libcap_source_dir}/libcap/../libcap/include/sys/capability.h" "${libcap_prefix}/include/sys/capability.h" - - cat > "${libcap_pkgconfig_dir}/libcap.pc" </dev/null; then - zig_bin="$(command -v zig)" - cc="${tool_root}/zigcc" - cxx="${tool_root}/zigcxx" - - cat >"${cc}" <"${cxx}" </dev/null || true)" -else - cc="${musl_linker}" - - if command -v "${arch}-linux-musl-g++" >/dev/null; then - cxx="$(command -v "${arch}-linux-musl-g++")" - elif command -v musl-g++ >/dev/null; then - cxx="$(command -v musl-g++)" - else - cxx="${cc}" - fi -fi - -if [[ -n "${sysroot}" && "${sysroot}" != "/" ]]; then - echo "BORING_BSSL_SYSROOT=${sysroot}" >> "$GITHUB_ENV" - boring_sysroot_var="BORING_BSSL_SYSROOT_${TARGET}" - boring_sysroot_var="${boring_sysroot_var//-/_}" - echo "${boring_sysroot_var}=${sysroot}" >> "$GITHUB_ENV" -fi - -cflags="-pthread" -cxxflags="-pthread" -if [[ "${TARGET}" == "aarch64-unknown-linux-musl" ]]; then - # BoringSSL enables -Wframe-larger-than=25344 under clang and treats warnings as errors. - cflags="${cflags} -Wno-error=frame-larger-than" - cxxflags="${cxxflags} -Wno-error=frame-larger-than" -fi - -echo "CFLAGS=${cflags}" >> "$GITHUB_ENV" -echo "CXXFLAGS=${cxxflags}" >> "$GITHUB_ENV" -echo "CC=${cc}" >> "$GITHUB_ENV" -echo "TARGET_CC=${cc}" >> "$GITHUB_ENV" -target_cc_var="CC_${TARGET}" -target_cc_var="${target_cc_var//-/_}" -echo "${target_cc_var}=${cc}" >> "$GITHUB_ENV" -echo "CXX=${cxx}" >> "$GITHUB_ENV" -echo "TARGET_CXX=${cxx}" >> "$GITHUB_ENV" -target_cxx_var="CXX_${TARGET}" -target_cxx_var="${target_cxx_var//-/_}" -echo "${target_cxx_var}=${cxx}" >> "$GITHUB_ENV" - -cargo_linker_var="CARGO_TARGET_${TARGET^^}_LINKER" -cargo_linker_var="${cargo_linker_var//-/_}" -echo "${cargo_linker_var}=${musl_linker}" >> "$GITHUB_ENV" - -echo "CMAKE_C_COMPILER=${cc}" >> "$GITHUB_ENV" -echo "CMAKE_CXX_COMPILER=${cxx}" >> "$GITHUB_ENV" -echo "CMAKE_ARGS=-DCMAKE_HAVE_THREADS_LIBRARY=1 -DCMAKE_USE_PTHREADS_INIT=1 -DCMAKE_THREAD_LIBS_INIT=-pthread -DTHREADS_PREFER_PTHREAD_FLAG=ON" >> "$GITHUB_ENV" - -# Allow pkg-config resolution during cross-compilation. -echo "PKG_CONFIG_ALLOW_CROSS=1" >> "$GITHUB_ENV" -pkg_config_path="${libcap_pkgconfig_dir}" -if [[ -n "${PKG_CONFIG_PATH:-}" ]]; then - pkg_config_path="${pkg_config_path}:${PKG_CONFIG_PATH}" -fi -echo "PKG_CONFIG_PATH=${pkg_config_path}" >> "$GITHUB_ENV" -pkg_config_path_var="PKG_CONFIG_PATH_${TARGET}" -pkg_config_path_var="${pkg_config_path_var//-/_}" -echo "${pkg_config_path_var}=${libcap_pkgconfig_dir}" >> "$GITHUB_ENV" - -if [[ -n "${sysroot}" && "${sysroot}" != "/" ]]; then - echo "PKG_CONFIG_SYSROOT_DIR=${sysroot}" >> "$GITHUB_ENV" - pkg_config_sysroot_var="PKG_CONFIG_SYSROOT_DIR_${TARGET}" - pkg_config_sysroot_var="${pkg_config_sysroot_var//-/_}" - echo "${pkg_config_sysroot_var}=${sysroot}" >> "$GITHUB_ENV" -fi diff --git a/reference/openai-codex/.github/scripts/run-argument-comment-lint-bazel.sh b/reference/openai-codex/.github/scripts/run-argument-comment-lint-bazel.sh deleted file mode 100755 index fddca4c..0000000 --- a/reference/openai-codex/.github/scripts/run-argument-comment-lint-bazel.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -bazel_lint_args=("$@") -if [[ "${RUNNER_OS:-}" == "Windows" ]]; then - has_host_platform_override=0 - for arg in "${bazel_lint_args[@]}"; do - if [[ "$arg" == --host_platform=* ]]; then - has_host_platform_override=1 - break - fi - done - - if [[ $has_host_platform_override -eq 0 ]]; then - # The nightly Windows lint toolchain is registered with an MSVC exec - # platform even though the lint target platform stays on `windows-gnullvm`. - # Override the host platform here so the exec-side helper binaries actually - # match the registered toolchain set. - bazel_lint_args+=("--host_platform=//:local_windows_msvc") - fi - - # Native Windows lint runs need exec-side Rust helper binaries and proc-macros - # to use rust-lld instead of the C++ linker path. The default `none` - # preference resolves to `cc` when a cc_toolchain is present, which currently - # routes these exec actions through clang++ with an argument shape it cannot - # consume. - bazel_lint_args+=("--@rules_rust//rust/settings:toolchain_linker_preference=rust") - - # Some Rust top-level targets are still intentionally incompatible with the - # local Windows MSVC exec platform. Skip those explicit targets so the native - # lint aspect can run across the compatible crate graph instead of failing the - # whole build after analysis. - bazel_lint_args+=("--skip_incompatible_explicit_targets") -fi - -read_query_labels() { - local query="$1" - local query_stdout - local query_stderr - query_stdout="$(mktemp)" - query_stderr="$(mktemp)" - - if ! ./.github/scripts/run-bazel-query-ci.sh \ - --keep_going \ - --output=label \ - -- "$query" >"$query_stdout" 2>"$query_stderr"; then - cat "$query_stderr" >&2 - rm -f "$query_stdout" "$query_stderr" - exit 1 - fi - - cat "$query_stdout" - rm -f "$query_stdout" "$query_stderr" -} - -final_build_targets=(//codex-rs/...) -if [[ "${RUNNER_OS:-}" == "Windows" ]]; then - # Bazel's local Windows platform currently lacks a default test toolchain for - # `rust_test`, so target the concrete Rust crate rules directly. The lint - # aspect still walks their crate graph, which preserves incremental reuse for - # non-test code while avoiding non-Rust wrapper targets such as platform_data. - final_build_targets=() - while IFS= read -r label; do - [[ -n "$label" ]] || continue - final_build_targets+=("$label") - done < <(read_query_labels 'kind("rust_(library|binary|proc_macro) rule", //codex-rs/...)') - - if [[ ${#final_build_targets[@]} -eq 0 ]]; then - echo "Failed to discover Windows Bazel lint targets." >&2 - exit 1 - fi -fi - -./.github/scripts/run-bazel-ci.sh \ - -- \ - build \ - "${bazel_lint_args[@]}" \ - -- \ - "${final_build_targets[@]}" diff --git a/reference/openai-codex/.github/scripts/run-bazel-ci.sh b/reference/openai-codex/.github/scripts/run-bazel-ci.sh deleted file mode 100755 index f98e4d8..0000000 --- a/reference/openai-codex/.github/scripts/run-bazel-ci.sh +++ /dev/null @@ -1,453 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -print_failed_bazel_test_logs=0 -print_failed_bazel_action_summary=0 -remote_download_toplevel=0 -windows_msvc_host_platform=0 -windows_cross_compile=0 - -while [[ $# -gt 0 ]]; do - case "$1" in - --print-failed-test-logs) - print_failed_bazel_test_logs=1 - shift - ;; - --print-failed-action-summary) - print_failed_bazel_action_summary=1 - shift - ;; - --remote-download-toplevel) - remote_download_toplevel=1 - shift - ;; - --windows-msvc-host-platform) - windows_msvc_host_platform=1 - shift - ;; - --windows-cross-compile) - windows_cross_compile=1 - shift - ;; - --) - shift - break - ;; - *) - echo "Unknown option: $1" >&2 - exit 1 - ;; - esac -done - -if [[ $# -eq 0 ]]; then - echo "Usage: $0 [--print-failed-test-logs] [--print-failed-action-summary] [--remote-download-toplevel] [--windows-msvc-host-platform] [--windows-cross-compile] -- -- " >&2 - exit 1 -fi - -bazel_startup_args=() -if [[ -n "${BAZEL_OUTPUT_USER_ROOT:-}" ]]; then - bazel_startup_args+=("--output_user_root=${BAZEL_OUTPUT_USER_ROOT}") -fi - -run_bazel() { - if [[ "${RUNNER_OS:-}" == "Windows" ]]; then - MSYS2_ARG_CONV_EXCL='*' bazel "$@" - return - fi - - bazel "$@" -} - -ci_config=ci-linux -case "${RUNNER_OS:-}" in - macOS) - ci_config=ci-macos - ;; - Windows) - if [[ $windows_cross_compile -eq 1 ]]; then - ci_config=ci-windows-cross - else - ci_config=ci-windows - fi - ;; -esac - -print_bazel_test_log_tails() { - local console_log="$1" - local testlogs_dir - local -a bazel_info_cmd=(bazel) - local -a bazel_info_args=(info) - - if (( ${#bazel_startup_args[@]} > 0 )); then - bazel_info_cmd+=("${bazel_startup_args[@]}") - fi - - # `bazel info` needs the same CI config as the failed test invocation so - # platform-specific output roots match. On Windows, omitting `ci-windows` - # would point at `local_windows-fastbuild` even when the test ran with the - # MSVC host platform under `local_windows_msvc-fastbuild`. - if [[ -n "${BUILDBUDDY_API_KEY:-}" ]]; then - bazel_info_args+=( - "--config=${ci_config}" - "--remote_header=x-buildbuddy-api-key=${BUILDBUDDY_API_KEY}" - ) - fi - # Only pass flags that affect Bazel's output-root selection or repository - # lookup. Test/build-only flags such as execution logs or remote download - # mode can make `bazel info` fail, which would hide the real test log path. - for arg in "${post_config_bazel_args[@]}"; do - case "$arg" in - --host_platform=* | --repo_contents_cache=* | --repository_cache=*) - bazel_info_args+=("$arg") - ;; - esac - done - - testlogs_dir="$(run_bazel "${bazel_info_cmd[@]:1}" \ - --noexperimental_remote_repo_contents_cache \ - "${bazel_info_args[@]}" \ - bazel-testlogs 2>/dev/null || echo bazel-testlogs)" - - local failed_targets=() - while IFS= read -r target; do - failed_targets+=("$target") - done < <( - grep -E '^(FAIL: //|ERROR: .* Testing //)' "$console_log" \ - | sed -E 's#^FAIL: (//[^ ]+).*#\1#; s#^ERROR: .* Testing (//[^ ]+) failed:.*#\1#' \ - | sort -u - ) - - if [[ ${#failed_targets[@]} -eq 0 ]]; then - echo "No failed Bazel test targets were found in console output." - return - fi - - for target in "${failed_targets[@]}"; do - local rel_path="${target#//}" - rel_path="${rel_path/://}" - local test_log="${testlogs_dir}/${rel_path}/test.log" - local reported_test_log - reported_test_log="$(grep -F "FAIL: ${target} " "$console_log" | sed -nE 's#.* \(see (.*[\\/]test\.log)\).*#\1#p' | head -n 1 || true)" - if [[ -n "$reported_test_log" ]]; then - reported_test_log="${reported_test_log//\\//}" - test_log="$reported_test_log" - fi - - echo "::group::Bazel test log tail for ${target}" - if [[ -f "$test_log" ]]; then - tail -n 200 "$test_log" - else - echo "Missing test log: $test_log" - fi - echo "::endgroup::" - done -} - -print_bazel_action_failure_summary() { - local console_log="$1" - local escaped_summary - local summary - - summary="$( - awk ' - function clean(line) { - gsub(sprintf("%c", 27) "\\[[0-9;]*m", "", line) - sub(/^.*\t[^\t]*\t[0-9TZ:._-]+ /, "", line) - return line - } - - function is_diagnostic(line) { - return line ~ /^(error(\[[^]]+\])?:|warning:|note:|help:)/ || - line ~ /^[[:space:]]+-->/ || - line ~ /^[[:space:]]*[0-9]+[[:space:]]+\|/ || - line ~ /^[[:space:]]*\|/ || - line ~ /^[[:space:]]+= (note|help):/ || - line ~ /^[[:space:]]*\^[[:space:]^~-]*$/ || - line ~ /^For more information/ || - line ~ /^error: aborting/ - } - - { - line = clean($0) - } - - line ~ /^ERROR: .* failed:/ { - if (printed) { - print "" - } - print line - in_failure = 1 - seen_diagnostic = 0 - printed = 1 - next - } - - in_failure && is_diagnostic(line) { - print line - seen_diagnostic = 1 - next - } - - in_failure && seen_diagnostic && line == "" { - print "" - next - } - - in_failure && seen_diagnostic { - in_failure = 0 - seen_diagnostic = 0 - next - } - ' "$console_log" - )" - - if [[ -z "$summary" ]]; then - summary="$(grep -E '^ERROR: |^FAILED: ' "$console_log" | tail -n 50 || true)" - fi - - if [[ -z "$summary" ]]; then - echo "No Bazel action failures were found in the captured console output." - return - fi - - if [[ "${GITHUB_ACTIONS:-}" == "true" ]]; then - escaped_summary="$( - printf '%s' "$summary" \ - | awk 'BEGIN { ORS = "" } { - gsub(/%/, "%25") - gsub(/\r/, "%0D") - print sep $0 - sep = "%0A" - }' - )" - echo "::error title=Bazel failed action diagnostics::${escaped_summary}" - fi - - echo - echo "Bazel failed action diagnostics:" - echo "--------------------------------" - printf '%s\n' "$summary" - echo "--------------------------------" -} - -bazel_args=() -bazel_targets=() -found_target_separator=0 -for arg in "$@"; do - if [[ "$arg" == "--" && $found_target_separator -eq 0 ]]; then - found_target_separator=1 - continue - fi - - if [[ $found_target_separator -eq 0 ]]; then - bazel_args+=("$arg") - else - bazel_targets+=("$arg") - fi -done - -if [[ ${#bazel_args[@]} -eq 0 || ${#bazel_targets[@]} -eq 0 ]]; then - echo "Expected Bazel args and targets separated by --" >&2 - exit 1 -fi - -if [[ "${RUNNER_OS:-}" == "Windows" && $windows_cross_compile -eq 1 && -z "${BUILDBUDDY_API_KEY:-}" ]]; then - # Fork PRs do not receive the BuildBuddy secret needed for the remote - # cross-compile config. Preserve the previous local Windows build shape. - windows_msvc_host_platform=1 -fi - -post_config_bazel_args=() -if [[ "${RUNNER_OS:-}" == "Windows" && $windows_msvc_host_platform -eq 1 ]]; then - has_host_platform_override=0 - for arg in "${bazel_args[@]}"; do - if [[ "$arg" == --host_platform=* ]]; then - has_host_platform_override=1 - break - fi - done - - if [[ $has_host_platform_override -eq 0 ]]; then - # Use the MSVC Windows platform for jobs that need helper binaries like - # Rust test wrappers and V8 generators to resolve a compatible toolchain. - # Callers that need a different Windows target platform should pass an - # explicit `--platforms=...` flag. - post_config_bazel_args+=("--host_platform=//:local_windows_msvc") - fi -fi - -if [[ $remote_download_toplevel -eq 1 ]]; then - # Override the CI config's remote_download_minimal setting when callers need - # the built artifact to exist on disk after the command completes. - post_config_bazel_args+=(--remote_download_toplevel) -fi - -if [[ "${RUNNER_OS:-}" == "Windows" && $windows_cross_compile -eq 1 && -n "${BUILDBUDDY_API_KEY:-}" ]]; then - # `--enable_platform_specific_config` expands `common:windows` on Windows - # hosts after ordinary rc configs, which can override `ci-windows-cross`'s - # RBE host platform. Repeat the host platform on the command line so V8 and - # other genrules execute on Linux RBE workers instead of Git Bash locally. - # - # Bazel also derives the default genrule shell from the client host. Without - # an explicit shell executable, remote Linux actions can be asked to run - # `C:\Program Files\Git\usr\bin\bash.exe`. - post_config_bazel_args+=(--host_platform=//:rbe --shell_executable=/bin/bash) -fi - -if [[ "${RUNNER_OS:-}" == "Windows" && $windows_cross_compile -eq 1 && -z "${BUILDBUDDY_API_KEY:-}" ]]; then - # The Windows cross-compile config depends on remote execution. Fork PRs do - # not receive the BuildBuddy secret, so fall back to the existing local build - # shape and keep its lower concurrency cap. - post_config_bazel_args+=(--jobs=8) -fi - -if [[ -n "${BAZEL_REPO_CONTENTS_CACHE:-}" ]]; then - # Windows self-hosted runners can run multiple Bazel jobs concurrently. Give - # each job its own repo contents cache so they do not fight over the shared - # path configured in `ci-windows`. - post_config_bazel_args+=("--repo_contents_cache=${BAZEL_REPO_CONTENTS_CACHE}") -fi - -if [[ -n "${BAZEL_REPOSITORY_CACHE:-}" ]]; then - post_config_bazel_args+=("--repository_cache=${BAZEL_REPOSITORY_CACHE}") -fi - -if [[ -n "${CODEX_BAZEL_EXECUTION_LOG_COMPACT_DIR:-}" ]]; then - post_config_bazel_args+=( - "--execution_log_compact_file=${CODEX_BAZEL_EXECUTION_LOG_COMPACT_DIR}/execution-log-${bazel_args[0]}-${GITHUB_JOB:-local}-$$.zst" - ) -fi - -if [[ "${RUNNER_OS:-}" == "Windows" ]]; then - pass_windows_build_env=1 - if [[ $windows_cross_compile -eq 1 && -n "${BUILDBUDDY_API_KEY:-}" ]]; then - # Remote build actions execute on Linux RBE workers. Passing the Windows - # runner's build environment there makes Bazel genrules try to execute - # C:\Program Files\Git\usr\bin\bash.exe on Linux. - pass_windows_build_env=0 - fi - - if [[ $pass_windows_build_env -eq 1 ]]; then - windows_action_env_vars=( - INCLUDE - LIB - LIBPATH - UCRTVersion - UniversalCRTSdkDir - VCINSTALLDIR - VCToolsInstallDir - WindowsLibPath - WindowsSdkBinPath - WindowsSdkDir - WindowsSDKLibVersion - WindowsSDKVersion - ) - - for env_var in "${windows_action_env_vars[@]}"; do - if [[ -n "${!env_var:-}" ]]; then - post_config_bazel_args+=("--action_env=${env_var}" "--host_action_env=${env_var}") - fi - done - fi - - if [[ -z "${CODEX_BAZEL_WINDOWS_PATH:-}" ]]; then - echo "CODEX_BAZEL_WINDOWS_PATH must be set for Windows Bazel CI." >&2 - exit 1 - fi - - if [[ $pass_windows_build_env -eq 1 ]]; then - post_config_bazel_args+=( - "--action_env=PATH=${CODEX_BAZEL_WINDOWS_PATH}" - "--host_action_env=PATH=${CODEX_BAZEL_WINDOWS_PATH}" - ) - elif [[ $windows_cross_compile -eq 1 ]]; then - # Remote build actions run on Linux RBE workers. Give their shell snippets - # a Linux PATH while preserving CODEX_BAZEL_WINDOWS_PATH below for local - # Windows test execution. - post_config_bazel_args+=( - "--action_env=PATH=/usr/bin:/bin" - "--host_action_env=PATH=/usr/bin:/bin" - ) - fi - post_config_bazel_args+=("--test_env=PATH=${CODEX_BAZEL_WINDOWS_PATH}") -fi - -bazel_console_log="$(mktemp)" -trap 'rm -f "$bazel_console_log"' EXIT - -bazel_cmd=(bazel) -if (( ${#bazel_startup_args[@]} > 0 )); then - bazel_cmd+=("${bazel_startup_args[@]}") -fi - -if [[ -n "${BUILDBUDDY_API_KEY:-}" ]]; then - echo "BuildBuddy API key is available; using remote Bazel configuration." - # Work around Bazel 9 remote repo contents cache / overlay materialization failures - # seen in CI (for example "is not a symlink" or permission errors while - # materializing external repos such as rules_perl). We still use BuildBuddy for - # remote execution/cache; this only disables the startup-level repo contents cache. - bazel_run_args=( - "${bazel_args[@]}" - "--config=${ci_config}" - "--remote_header=x-buildbuddy-api-key=${BUILDBUDDY_API_KEY}" - ) - if (( ${#post_config_bazel_args[@]} > 0 )); then - bazel_run_args+=("${post_config_bazel_args[@]}") - fi - set +e - run_bazel "${bazel_cmd[@]:1}" \ - --noexperimental_remote_repo_contents_cache \ - "${bazel_run_args[@]}" \ - -- \ - "${bazel_targets[@]}" \ - 2>&1 | tee "$bazel_console_log" - bazel_status=${PIPESTATUS[0]} - set -e -else - echo "BuildBuddy API key is not available; using local Bazel configuration." - # Keep fork/community PRs on Bazel but disable remote services that are - # configured in .bazelrc and require auth. - # - # Flag docs: - # - Command-line reference: https://bazel.build/reference/command-line-reference - # - Remote caching overview: https://bazel.build/remote/caching - # - Remote execution overview: https://bazel.build/remote/rbe - # - Build Event Protocol overview: https://bazel.build/remote/bep - # - # --noexperimental_remote_repo_contents_cache: - # disable remote repo contents cache enabled in .bazelrc startup options. - # https://bazel.build/reference/command-line-reference#startup_options-flag--experimental_remote_repo_contents_cache - # --remote_cache= and --remote_executor=: - # clear remote cache/execution endpoints configured in .bazelrc. - # https://bazel.build/reference/command-line-reference#common_options-flag--remote_cache - # https://bazel.build/reference/command-line-reference#common_options-flag--remote_executor - bazel_run_args=( - "${bazel_args[@]}" - --remote_cache= - --remote_executor= - ) - if (( ${#post_config_bazel_args[@]} > 0 )); then - bazel_run_args+=("${post_config_bazel_args[@]}") - fi - set +e - run_bazel "${bazel_cmd[@]:1}" \ - --noexperimental_remote_repo_contents_cache \ - "${bazel_run_args[@]}" \ - -- \ - "${bazel_targets[@]}" \ - 2>&1 | tee "$bazel_console_log" - bazel_status=${PIPESTATUS[0]} - set -e -fi - -if [[ ${bazel_status:-0} -ne 0 ]]; then - if [[ $print_failed_bazel_action_summary -eq 1 ]]; then - print_bazel_action_failure_summary "$bazel_console_log" - fi - if [[ $print_failed_bazel_test_logs -eq 1 ]]; then - print_bazel_test_log_tails "$bazel_console_log" - fi - exit "$bazel_status" -fi diff --git a/reference/openai-codex/.github/scripts/run-bazel-query-ci.sh b/reference/openai-codex/.github/scripts/run-bazel-query-ci.sh deleted file mode 100755 index dd03b67..0000000 --- a/reference/openai-codex/.github/scripts/run-bazel-query-ci.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# Run Bazel queries with the same CI startup settings as the main build/test -# invocation so target-discovery queries can reuse the same Bazel server. - -query_args=() -windows_cross_compile=0 -while [[ $# -gt 0 ]]; do - case "$1" in - --windows-cross-compile) - windows_cross_compile=1 - shift - ;; - --) - shift - break - ;; - *) - query_args+=("$1") - shift - ;; - esac -done - -if [[ $# -ne 1 ]]; then - echo "Usage: $0 [--windows-cross-compile] [...] -- " >&2 - exit 1 -fi - -query_expression="$1" - -ci_config=ci-linux -case "${RUNNER_OS:-}" in - macOS) - ci_config=ci-macos - ;; - Windows) - if [[ $windows_cross_compile -eq 1 ]]; then - ci_config=ci-windows-cross - else - ci_config=ci-windows - fi - ;; -esac - -bazel_startup_args=() -if [[ -n "${BAZEL_OUTPUT_USER_ROOT:-}" ]]; then - bazel_startup_args+=("--output_user_root=${BAZEL_OUTPUT_USER_ROOT}") -fi - -run_bazel() { - if [[ "${RUNNER_OS:-}" == "Windows" ]]; then - MSYS2_ARG_CONV_EXCL='*' bazel "$@" - return - fi - - bazel "$@" -} - -bazel_query_args=(--noexperimental_remote_repo_contents_cache query) -if [[ -n "${BUILDBUDDY_API_KEY:-}" ]]; then - bazel_query_args+=( - "--config=${ci_config}" - "--remote_header=x-buildbuddy-api-key=${BUILDBUDDY_API_KEY}" - ) -fi - -if [[ -n "${BAZEL_REPO_CONTENTS_CACHE:-}" ]]; then - bazel_query_args+=("--repo_contents_cache=${BAZEL_REPO_CONTENTS_CACHE}") -fi - -if [[ -n "${BAZEL_REPOSITORY_CACHE:-}" ]]; then - bazel_query_args+=("--repository_cache=${BAZEL_REPOSITORY_CACHE}") -fi - -bazel_query_args+=("${query_args[@]}" "$query_expression") - -if (( ${#bazel_startup_args[@]} > 0 )); then - run_bazel "${bazel_startup_args[@]}" "${bazel_query_args[@]}" -else - run_bazel "${bazel_query_args[@]}" -fi diff --git a/reference/openai-codex/.github/scripts/rusty_v8_bazel.py b/reference/openai-codex/.github/scripts/rusty_v8_bazel.py deleted file mode 100644 index 2f46daf..0000000 --- a/reference/openai-codex/.github/scripts/rusty_v8_bazel.py +++ /dev/null @@ -1,412 +0,0 @@ -#!/usr/bin/env python3 - -from __future__ import annotations - -import argparse -import gzip -import hashlib -import os -import re -import shutil -import subprocess -import sys -import tomllib -from pathlib import Path - -from rusty_v8_module_bazel import ( - RustyV8ChecksumError, - check_module_bazel, - rusty_v8_http_file_versions, - update_module_bazel, -) - - -ROOT = Path(__file__).resolve().parents[2] -MODULE_BAZEL = ROOT / "MODULE.bazel" -RUSTY_V8_CHECKSUMS_DIR = ROOT / "third_party" / "v8" -RELEASE_ARTIFACT_PROFILE = "release" -SANDBOX_ARTIFACT_PROFILE = "ptrcomp_sandbox_release" -ARTIFACT_BAZEL_CONFIGS = ["rusty-v8-upstream-libcxx"] - - -def bazel_remote_args() -> list[str]: - buildbuddy_api_key = os.environ.get("BUILDBUDDY_API_KEY") - if not buildbuddy_api_key: - return [] - return [f"--remote_header=x-buildbuddy-api-key={buildbuddy_api_key}"] - - -def bazel_execroot() -> Path: - result = subprocess.run( - ["bazel", "info", "execution_root"], - cwd=ROOT, - check=True, - capture_output=True, - text=True, - ) - return Path(result.stdout.strip()) - - -def bazel_output_base() -> Path: - result = subprocess.run( - ["bazel", "info", "output_base"], - cwd=ROOT, - check=True, - capture_output=True, - text=True, - ) - return Path(result.stdout.strip()) - - -def bazel_output_path(path: str) -> Path: - if path.startswith("external/"): - return bazel_output_base() / path - return bazel_execroot() / path - - -def bazel_output_files( - platform: str, - labels: list[str], - compilation_mode: str = "fastbuild", - bazel_configs: list[str] | None = None, -) -> list[Path]: - expression = "set(" + " ".join(labels) + ")" - bazel_configs = bazel_configs or [] - result = subprocess.run( - [ - "bazel", - "cquery", - "-c", - compilation_mode, - f"--platforms=@llvm//platforms:{platform}", - *[f"--config={config}" for config in bazel_configs], - *bazel_remote_args(), - "--output=files", - expression, - ], - cwd=ROOT, - check=True, - capture_output=True, - text=True, - ) - return [bazel_output_path(line.strip()) for line in result.stdout.splitlines() if line.strip()] - - -def bazel_build( - platform: str, - labels: list[str], - compilation_mode: str = "fastbuild", - bazel_configs: list[str] | None = None, - download_toplevel: bool = False, -) -> None: - bazel_configs = bazel_configs or [] - download_args = ["--remote_download_toplevel"] if download_toplevel else [] - subprocess.run( - [ - "bazel", - "build", - "-c", - compilation_mode, - f"--platforms=@llvm//platforms:{platform}", - *[f"--config={config}" for config in bazel_configs], - *bazel_remote_args(), - *download_args, - *labels, - ], - cwd=ROOT, - check=True, - ) - - -def ensure_bazel_output_files( - platform: str, - labels: list[str], - compilation_mode: str = "fastbuild", - bazel_configs: list[str] | None = None, -) -> list[Path]: - # Bazel output paths can be reused across config flips, so existence alone - # does not prove the files match the requested flags. - bazel_build( - platform, - labels, - compilation_mode, - bazel_configs, - download_toplevel=True, - ) - outputs = bazel_output_files(platform, labels, compilation_mode, bazel_configs) - missing = [str(path) for path in outputs if not path.exists()] - if missing: - raise SystemExit(f"missing built outputs for {labels}: {missing}") - return outputs - - -def artifact_bazel_configs(bazel_configs: list[str] | None = None) -> list[str]: - configured = list(ARTIFACT_BAZEL_CONFIGS) - for config in bazel_configs or []: - if config not in configured: - configured.append(config) - return configured - - -def release_pair_label(target: str, sandbox: bool = False) -> str: - target_suffix = target.replace("-", "_") - pair_kind = "sandbox_release_pair" if sandbox else "release_pair" - return f"//third_party/v8:rusty_v8_{pair_kind}_{target_suffix}" - - -def resolved_v8_crate_version() -> str: - cargo_lock = tomllib.loads((ROOT / "codex-rs" / "Cargo.lock").read_text()) - versions = sorted( - { - package["version"] - for package in cargo_lock["package"] - if package["name"] == "v8" - } - ) - if len(versions) == 1: - return versions[0] - if len(versions) > 1: - raise SystemExit(f"expected exactly one resolved v8 version, found: {versions}") - - module_bazel = (ROOT / "MODULE.bazel").read_text() - matches = sorted( - set( - re.findall( - r'https://static\.crates\.io/crates/v8/v8-([0-9]+\.[0-9]+\.[0-9]+)\.crate', - module_bazel, - ) - ) - ) - if len(matches) != 1: - raise SystemExit( - "expected exactly one pinned v8 crate version in MODULE.bazel, " - f"found: {matches}" - ) - return matches[0] - - -def rusty_v8_checksum_manifest_path(version: str) -> Path: - return RUSTY_V8_CHECKSUMS_DIR / f"rusty_v8_{version.replace('.', '_')}.sha256" - - -def command_version(version: str | None) -> str: - if version is not None: - return version - - manifest_versions = rusty_v8_http_file_versions(MODULE_BAZEL.read_text()) - if len(manifest_versions) == 1: - return manifest_versions[0] - if len(manifest_versions) > 1: - raise SystemExit( - "expected at most one rusty_v8 http_file version in MODULE.bazel, " - f"found: {manifest_versions}; pass --version explicitly" - ) - - return resolved_v8_crate_version() - - -def command_manifest_path(manifest: Path | None, version: str) -> Path: - if manifest is None: - return rusty_v8_checksum_manifest_path(version) - if manifest.is_absolute(): - return manifest - return ROOT / manifest - - -def staged_archive_name(target: str, source_path: Path, artifact_profile: str) -> str: - if target.endswith("-pc-windows-msvc"): - return f"rusty_v8_{artifact_profile}_{target}.lib.gz" - return f"librusty_v8_{artifact_profile}_{target}.a.gz" - - -def staged_binding_name(target: str, artifact_profile: str) -> str: - return f"src_binding_{artifact_profile}_{target}.rs" - - -def staged_checksums_name(target: str, artifact_profile: str) -> str: - return f"rusty_v8_{artifact_profile}_{target}.sha256" - - -def stage_artifacts( - target: str, - lib_path: Path, - binding_path: Path, - output_dir: Path, - sandbox: bool, -) -> None: - missing_paths = [str(path) for path in [lib_path, binding_path] if not path.exists()] - if missing_paths: - raise SystemExit(f"missing release outputs for {target}: {missing_paths}") - - output_dir.mkdir(parents=True, exist_ok=True) - artifact_profile = SANDBOX_ARTIFACT_PROFILE if sandbox else RELEASE_ARTIFACT_PROFILE - staged_library = output_dir / staged_archive_name(target, lib_path, artifact_profile) - staged_binding = output_dir / staged_binding_name(target, artifact_profile) - - with lib_path.open("rb") as src, staged_library.open("wb") as dst: - with gzip.GzipFile( - filename="", - mode="wb", - fileobj=dst, - compresslevel=6, - mtime=0, - ) as gz: - shutil.copyfileobj(src, gz) - - shutil.copyfile(binding_path, staged_binding) - - staged_checksums = output_dir / staged_checksums_name(target, artifact_profile) - with staged_checksums.open("w", encoding="utf-8") as checksums: - for path in [staged_library, staged_binding]: - digest = hashlib.sha256() - with path.open("rb") as artifact: - for chunk in iter(lambda: artifact.read(1024 * 1024), b""): - digest.update(chunk) - checksums.write(f"{digest.hexdigest()} {path.name}\n") - - print(staged_library) - print(staged_binding) - print(staged_checksums) - - -def upstream_release_pair_paths(source_root: Path, target: str) -> tuple[Path, Path]: - lib_name = "rusty_v8.lib" if target.endswith("-pc-windows-msvc") else "librusty_v8.a" - gn_out = source_root / "target" / target / "release" / "gn_out" - return gn_out / "obj" / lib_name, gn_out / "src_binding.rs" - - -def stage_upstream_release_pair( - source_root: Path, - target: str, - output_dir: Path, - sandbox: bool = False, -) -> None: - lib_path, binding_path = upstream_release_pair_paths(source_root, target) - stage_artifacts(target, lib_path, binding_path, output_dir, sandbox) - - -def stage_release_pair( - platform: str, - target: str, - output_dir: Path, - compilation_mode: str = "fastbuild", - bazel_configs: list[str] | None = None, - sandbox: bool = False, -) -> None: - bazel_configs = artifact_bazel_configs(bazel_configs) - outputs = ensure_bazel_output_files( - platform, - [release_pair_label(target, sandbox)], - compilation_mode, - bazel_configs, - ) - - try: - lib_path = next(path for path in outputs if path.suffix in {".a", ".lib"}) - except StopIteration as exc: - raise SystemExit(f"missing static library output for {target}") from exc - - try: - binding_path = next(path for path in outputs if path.suffix == ".rs") - except StopIteration as exc: - raise SystemExit(f"missing Rust binding output for {target}") from exc - - stage_artifacts(target, lib_path, binding_path, output_dir, sandbox) - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest="command", required=True) - - stage_release_pair_parser = subparsers.add_parser("stage-release-pair") - stage_release_pair_parser.add_argument("--platform", required=True) - stage_release_pair_parser.add_argument("--target", required=True) - stage_release_pair_parser.add_argument("--output-dir", required=True) - stage_release_pair_parser.add_argument("--sandbox", action="store_true") - stage_release_pair_parser.add_argument( - "--bazel-config", - action="append", - default=[], - dest="bazel_configs", - ) - stage_release_pair_parser.add_argument( - "--compilation-mode", - default="fastbuild", - choices=["fastbuild", "opt", "dbg"], - ) - - stage_upstream_release_pair_parser = subparsers.add_parser( - "stage-upstream-release-pair" - ) - stage_upstream_release_pair_parser.add_argument("--source-root", type=Path, required=True) - stage_upstream_release_pair_parser.add_argument("--target", required=True) - stage_upstream_release_pair_parser.add_argument("--output-dir", required=True) - stage_upstream_release_pair_parser.add_argument("--sandbox", action="store_true") - - subparsers.add_parser("resolved-v8-crate-version") - - check_module_bazel_parser = subparsers.add_parser("check-module-bazel") - check_module_bazel_parser.add_argument("--version") - check_module_bazel_parser.add_argument("--manifest", type=Path) - check_module_bazel_parser.add_argument( - "--module-bazel", - type=Path, - default=MODULE_BAZEL, - ) - - update_module_bazel_parser = subparsers.add_parser("update-module-bazel") - update_module_bazel_parser.add_argument("--version") - update_module_bazel_parser.add_argument("--manifest", type=Path) - update_module_bazel_parser.add_argument( - "--module-bazel", - type=Path, - default=MODULE_BAZEL, - ) - - return parser.parse_args() - - -def main() -> int: - args = parse_args() - if args.command == "stage-release-pair": - stage_release_pair( - platform=args.platform, - target=args.target, - output_dir=Path(args.output_dir), - compilation_mode=args.compilation_mode, - bazel_configs=args.bazel_configs, - sandbox=args.sandbox, - ) - return 0 - if args.command == "stage-upstream-release-pair": - stage_upstream_release_pair( - source_root=args.source_root, - target=args.target, - output_dir=Path(args.output_dir), - sandbox=args.sandbox, - ) - return 0 - if args.command == "resolved-v8-crate-version": - print(resolved_v8_crate_version()) - return 0 - if args.command == "check-module-bazel": - version = command_version(args.version) - manifest_path = command_manifest_path(args.manifest, version) - try: - check_module_bazel(args.module_bazel, manifest_path, version) - except RustyV8ChecksumError as exc: - raise SystemExit(str(exc)) from exc - return 0 - if args.command == "update-module-bazel": - version = command_version(args.version) - manifest_path = command_manifest_path(args.manifest, version) - try: - update_module_bazel(args.module_bazel, manifest_path, version) - except RustyV8ChecksumError as exc: - raise SystemExit(str(exc)) from exc - return 0 - raise SystemExit(f"unsupported command: {args.command}") - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/reference/openai-codex/.github/scripts/rusty_v8_module_bazel.py b/reference/openai-codex/.github/scripts/rusty_v8_module_bazel.py deleted file mode 100644 index 5d5dec0..0000000 --- a/reference/openai-codex/.github/scripts/rusty_v8_module_bazel.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/env python3 - -from __future__ import annotations - -import re -from dataclasses import dataclass -from pathlib import Path - - -SHA256_RE = re.compile(r"[0-9a-f]{64}") -HTTP_FILE_BLOCK_RE = re.compile(r"(?ms)^http_file\(\n.*?^\)\n?") -HTTP_FILE_VERSION_RE = re.compile(r"^rusty_v8_([0-9]+)_([0-9]+)_([0-9]+)_") - - -class RustyV8ChecksumError(ValueError): - pass - - -@dataclass(frozen=True) -class RustyV8HttpFile: - start: int - end: int - block: str - name: str - downloaded_file_path: str - sha256: str | None - - -def parse_checksum_manifest(path: Path) -> dict[str, str]: - try: - lines = path.read_text(encoding="utf-8").splitlines() - except FileNotFoundError as exc: - raise RustyV8ChecksumError(f"missing checksum manifest: {path}") from exc - - checksums: dict[str, str] = {} - for line_number, line in enumerate(lines, 1): - if not line.strip(): - continue - parts = line.split() - if len(parts) != 2: - raise RustyV8ChecksumError( - f"{path}:{line_number}: expected ' '" - ) - checksum, filename = parts - if not SHA256_RE.fullmatch(checksum): - raise RustyV8ChecksumError( - f"{path}:{line_number}: invalid SHA-256 digest for {filename}" - ) - if not filename or filename in {".", ".."} or "/" in filename: - raise RustyV8ChecksumError( - f"{path}:{line_number}: expected a bare artifact filename" - ) - if filename in checksums: - raise RustyV8ChecksumError( - f"{path}:{line_number}: duplicate checksum for {filename}" - ) - checksums[filename] = checksum - - if not checksums: - raise RustyV8ChecksumError(f"empty checksum manifest: {path}") - return checksums - - -def string_field(block: str, field: str) -> str | None: - # Matches one-line string fields inside http_file blocks, e.g. `sha256 = "...",`. - match = re.search(rf'^\s*{re.escape(field)}\s*=\s*"([^"]+)",\s*$', block, re.M) - if match: - return match.group(1) - return None - - -def rusty_v8_http_files(module_bazel: str, version: str) -> list[RustyV8HttpFile]: - version_slug = version.replace(".", "_") - name_prefix = f"rusty_v8_{version_slug}_" - entries = [] - for match in HTTP_FILE_BLOCK_RE.finditer(module_bazel): - block = match.group(0) - name = string_field(block, "name") - if not name or not name.startswith(name_prefix): - continue - downloaded_file_path = string_field(block, "downloaded_file_path") - if not downloaded_file_path: - raise RustyV8ChecksumError( - f"MODULE.bazel {name} is missing downloaded_file_path" - ) - entries.append( - RustyV8HttpFile( - start=match.start(), - end=match.end(), - block=block, - name=name, - downloaded_file_path=downloaded_file_path, - sha256=string_field(block, "sha256"), - ) - ) - return entries - - -def rusty_v8_http_file_versions(module_bazel: str) -> list[str]: - versions = set() - for match in HTTP_FILE_BLOCK_RE.finditer(module_bazel): - name = string_field(match.group(0), "name") - if not name: - continue - version_match = HTTP_FILE_VERSION_RE.match(name) - if version_match: - versions.add(".".join(version_match.groups())) - return sorted(versions) - - -def module_entry_set_errors( - entries: list[RustyV8HttpFile], - checksums: dict[str, str], - version: str, -) -> list[str]: - errors = [] - if not entries: - errors.append(f"MODULE.bazel has no rusty_v8 http_file entries for {version}") - return errors - - module_files: dict[str, RustyV8HttpFile] = {} - duplicate_files = set() - for entry in entries: - if entry.downloaded_file_path in module_files: - duplicate_files.add(entry.downloaded_file_path) - module_files[entry.downloaded_file_path] = entry - - for filename in sorted(duplicate_files): - errors.append(f"MODULE.bazel has duplicate http_file entries for {filename}") - - for filename in sorted(set(module_files) - set(checksums)): - entry = module_files[filename] - errors.append(f"MODULE.bazel {entry.name} has no checksum in the manifest") - - for filename in sorted(set(checksums) - set(module_files)): - errors.append(f"manifest has {filename}, but MODULE.bazel has no http_file") - - return errors - - -def module_checksum_errors( - entries: list[RustyV8HttpFile], - checksums: dict[str, str], -) -> list[str]: - errors = [] - for entry in entries: - expected = checksums.get(entry.downloaded_file_path) - if expected is None: - continue - if entry.sha256 is None: - errors.append(f"MODULE.bazel {entry.name} is missing sha256") - elif entry.sha256 != expected: - errors.append( - f"MODULE.bazel {entry.name} has sha256 {entry.sha256}, " - f"expected {expected}" - ) - return errors - - -def raise_checksum_errors(message: str, errors: list[str]) -> None: - if errors: - formatted_errors = "\n".join(f"- {error}" for error in errors) - raise RustyV8ChecksumError(f"{message}:\n{formatted_errors}") - - -def check_module_bazel_text( - module_bazel: str, - checksums: dict[str, str], - version: str, -) -> None: - entries = rusty_v8_http_files(module_bazel, version) - errors = [ - *module_entry_set_errors(entries, checksums, version), - *module_checksum_errors(entries, checksums), - ] - raise_checksum_errors("rusty_v8 MODULE.bazel checksum drift", errors) - - -def block_with_sha256(block: str, checksum: str) -> str: - sha256_line_re = re.compile(r'(?m)^(\s*)sha256\s*=\s*"[0-9a-f]+",\s*$') - if sha256_line_re.search(block): - return sha256_line_re.sub( - lambda match: f'{match.group(1)}sha256 = "{checksum}",', - block, - count=1, - ) - - downloaded_file_path_match = re.search( - r'(?m)^(\s*)downloaded_file_path\s*=\s*"[^"]+",\n', - block, - ) - if not downloaded_file_path_match: - raise RustyV8ChecksumError("http_file block is missing downloaded_file_path") - insert_at = downloaded_file_path_match.end() - indent = downloaded_file_path_match.group(1) - return f'{block[:insert_at]}{indent}sha256 = "{checksum}",\n{block[insert_at:]}' - - -def update_module_bazel_text( - module_bazel: str, - checksums: dict[str, str], - version: str, -) -> str: - entries = rusty_v8_http_files(module_bazel, version) - errors = module_entry_set_errors(entries, checksums, version) - raise_checksum_errors("cannot update rusty_v8 MODULE.bazel checksums", errors) - - updated = [] - previous_end = 0 - for entry in entries: - updated.append(module_bazel[previous_end : entry.start]) - updated.append( - block_with_sha256(entry.block, checksums[entry.downloaded_file_path]) - ) - previous_end = entry.end - updated.append(module_bazel[previous_end:]) - return "".join(updated) - - -def check_module_bazel( - module_bazel_path: Path, - manifest_path: Path, - version: str, -) -> None: - checksums = parse_checksum_manifest(manifest_path) - module_bazel = module_bazel_path.read_text(encoding="utf-8") - check_module_bazel_text(module_bazel, checksums, version) - print(f"{module_bazel_path} rusty_v8 {version} checksums match {manifest_path}") - - -def update_module_bazel( - module_bazel_path: Path, - manifest_path: Path, - version: str, -) -> None: - checksums = parse_checksum_manifest(manifest_path) - module_bazel = module_bazel_path.read_text(encoding="utf-8") - updated_module_bazel = update_module_bazel_text(module_bazel, checksums, version) - if updated_module_bazel == module_bazel: - print(f"{module_bazel_path} rusty_v8 {version} checksums are already current") - return - module_bazel_path.write_text(updated_module_bazel, encoding="utf-8") - print(f"updated {module_bazel_path} rusty_v8 {version} checksums") diff --git a/reference/openai-codex/.github/scripts/setup-dev-drive.ps1 b/reference/openai-codex/.github/scripts/setup-dev-drive.ps1 deleted file mode 100644 index 2b94e1b..0000000 --- a/reference/openai-codex/.github/scripts/setup-dev-drive.ps1 +++ /dev/null @@ -1,62 +0,0 @@ -# Configure a fast drive for Windows CI jobs. -# -# GitHub-hosted Windows runners do not always expose a secondary D: volume. When -# they do not, try to create a Dev Drive VHD and fall back to C: if the runner -# image does not allow that provisioning path. - -function Use-FallbackDrive { - param([string]$Reason) - - Write-Warning "$Reason Falling back to C:" - return "C:" -} - -function Invoke-BestEffort { - param([scriptblock]$Script, [string]$Description) - - try { - & $Script - } catch { - Write-Warning "$Description failed: $($_.Exception.Message)" - } -} - -if (Test-Path "D:\") { - Write-Output "Using existing drive at D:" - $Drive = "D:" -} else { - try { - $VhdPath = Join-Path $env:RUNNER_TEMP "codex-dev-drive.vhdx" - $SizeBytes = 64GB - - if (Test-Path $VhdPath) { - Remove-Item -Path $VhdPath -Force - } - - New-VHD -Path $VhdPath -SizeBytes $SizeBytes -Dynamic -ErrorAction Stop | Out-Null - $Mounted = Mount-VHD -Path $VhdPath -Passthru -ErrorAction Stop - $Disk = $Mounted | Get-Disk -ErrorAction Stop - $Disk | Initialize-Disk -PartitionStyle GPT -ErrorAction Stop - $Partition = $Disk | New-Partition -AssignDriveLetter -UseMaximumSize -ErrorAction Stop - $Volume = $Partition | Format-Volume -FileSystem ReFS -NewFileSystemLabel "CodexDevDrive" -DevDrive -Confirm:$false -Force -ErrorAction Stop - - $Drive = "$($Volume.DriveLetter):" - - Invoke-BestEffort { fsutil devdrv trust $Drive } "Trusting Dev Drive $Drive" - Invoke-BestEffort { fsutil devdrv enable /disallowAv } "Disabling AV filter attachment for Dev Drives" - Invoke-BestEffort { fsutil devdrv query $Drive } "Querying Dev Drive $Drive" - - Write-Output "Using Dev Drive at $Drive" - } catch { - $Drive = Use-FallbackDrive "Failed to create Dev Drive: $($_.Exception.Message)" - } -} - -$Tmp = "$Drive\codex-tmp" -New-Item -Path $Tmp -ItemType Directory -Force | Out-Null - -@( - "DEV_DRIVE=$Drive" - "TMP=$Tmp" - "TEMP=$Tmp" -) | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append diff --git a/reference/openai-codex/.github/scripts/test_rusty_v8_bazel.py b/reference/openai-codex/.github/scripts/test_rusty_v8_bazel.py deleted file mode 100644 index 19690db..0000000 --- a/reference/openai-codex/.github/scripts/test_rusty_v8_bazel.py +++ /dev/null @@ -1,413 +0,0 @@ -#!/usr/bin/env python3 - -from __future__ import annotations - -import textwrap -import unittest -from os import environ -from pathlib import Path -from tempfile import TemporaryDirectory -from unittest.mock import patch - -import rusty_v8_bazel -import rusty_v8_module_bazel - - -class RustyV8BazelTest(unittest.TestCase): - def test_consumer_selectors_track_resolved_crate_version(self) -> None: - build_bazel = ( - rusty_v8_bazel.ROOT / "third_party" / "v8" / "BUILD.bazel" - ).read_text() - version_suffix = rusty_v8_bazel.resolved_v8_crate_version().replace(".", "_") - - for selector in [ - "aarch64_apple_darwin_bazel", - "aarch64_pc_windows_gnullvm", - "aarch64_pc_windows_msvc", - "aarch64_unknown_linux_gnu_bazel", - "aarch64_unknown_linux_musl_release_base", - "x86_64_apple_darwin_bazel", - "x86_64_pc_windows_gnullvm", - "x86_64_pc_windows_msvc", - "x86_64_unknown_linux_gnu_bazel", - "x86_64_unknown_linux_musl_release", - ]: - self.assertIn( - f":v8_{version_suffix}_{selector}", - build_bazel, - ) - - for selector in [ - "aarch64_apple_darwin", - "aarch64_pc_windows_gnullvm", - "aarch64_pc_windows_msvc", - "aarch64_unknown_linux_gnu", - "aarch64_unknown_linux_musl", - "x86_64_apple_darwin", - "x86_64_pc_windows_gnullvm", - "x86_64_pc_windows_msvc", - "x86_64_unknown_linux_gnu", - "x86_64_unknown_linux_musl", - ]: - self.assertIn( - f":src_binding_release_{selector}_{version_suffix}_release", - build_bazel, - ) - - def test_command_version_tracks_remaining_http_file_assets(self) -> None: - with TemporaryDirectory() as temp_dir: - module_bazel = Path(temp_dir) / "MODULE.bazel" - module_bazel.write_text( - textwrap.dedent( - """\ - http_file( - name = "rusty_v8_146_4_0_x86_64_unknown_linux_gnu_archive", - downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz", - urls = ["https://example.test/archive.gz"], - ) - """ - ) - ) - - with patch.object(rusty_v8_bazel, "MODULE_BAZEL", module_bazel): - self.assertEqual("146.4.0", rusty_v8_bazel.command_version(None)) - - def test_artifact_bazel_configs_always_enable_upstream_libcxx(self) -> None: - self.assertEqual( - ["rusty-v8-upstream-libcxx"], - rusty_v8_bazel.artifact_bazel_configs(), - ) - self.assertEqual( - ["rusty-v8-upstream-libcxx", "v8-release-compat"], - rusty_v8_bazel.artifact_bazel_configs(["v8-release-compat"]), - ) - self.assertEqual( - ["rusty-v8-upstream-libcxx", "v8-release-compat"], - rusty_v8_bazel.artifact_bazel_configs( - ["rusty-v8-upstream-libcxx", "v8-release-compat"] - ), - ) - - def test_bazel_remote_args_include_buildbuddy_header_when_present(self) -> None: - with patch.dict(environ, {"BUILDBUDDY_API_KEY": "token"}, clear=False): - self.assertEqual( - ["--remote_header=x-buildbuddy-api-key=token"], - rusty_v8_bazel.bazel_remote_args(), - ) - - with patch.dict(environ, {}, clear=True): - self.assertEqual([], rusty_v8_bazel.bazel_remote_args()) - - def test_release_pair_labels_and_staged_names_distinguish_sandbox_artifacts(self) -> None: - self.assertEqual( - "//third_party/v8:rusty_v8_release_pair_x86_64_unknown_linux_musl", - rusty_v8_bazel.release_pair_label("x86_64-unknown-linux-musl"), - ) - self.assertEqual( - "//third_party/v8:rusty_v8_sandbox_release_pair_x86_64_unknown_linux_musl", - rusty_v8_bazel.release_pair_label("x86_64-unknown-linux-musl", sandbox=True), - ) - self.assertEqual( - "//third_party/v8:rusty_v8_sandbox_release_pair_x86_64_apple_darwin", - rusty_v8_bazel.release_pair_label("x86_64-apple-darwin", sandbox=True), - ) - self.assertEqual( - "librusty_v8_release_x86_64-unknown-linux-musl.a.gz", - rusty_v8_bazel.staged_archive_name( - "x86_64-unknown-linux-musl", - Path("libv8.a"), - rusty_v8_bazel.RELEASE_ARTIFACT_PROFILE, - ), - ) - self.assertEqual( - "rusty_v8_ptrcomp_sandbox_release_x86_64-pc-windows-msvc.lib.gz", - rusty_v8_bazel.staged_archive_name( - "x86_64-pc-windows-msvc", - Path("v8.a"), - rusty_v8_bazel.SANDBOX_ARTIFACT_PROFILE, - ), - ) - self.assertEqual( - "src_binding_ptrcomp_sandbox_release_x86_64-unknown-linux-musl.rs", - rusty_v8_bazel.staged_binding_name( - "x86_64-unknown-linux-musl", - rusty_v8_bazel.SANDBOX_ARTIFACT_PROFILE, - ), - ) - self.assertEqual( - "rusty_v8_ptrcomp_sandbox_release_x86_64-unknown-linux-musl.sha256", - rusty_v8_bazel.staged_checksums_name( - "x86_64-unknown-linux-musl", - rusty_v8_bazel.SANDBOX_ARTIFACT_PROFILE, - ), - ) - - def test_stage_artifacts(self) -> None: - with TemporaryDirectory() as source_dir, TemporaryDirectory() as output_dir: - source_root = Path(source_dir) - archive = source_root / "librusty_v8.a" - binding = source_root / "src_binding.rs" - archive.write_bytes(b"archive") - binding.write_text("binding") - - rusty_v8_bazel.stage_artifacts( - "aarch64-apple-darwin", - archive, - binding, - Path(output_dir), - sandbox=True, - ) - - self.assertEqual( - { - "librusty_v8_ptrcomp_sandbox_release_aarch64-apple-darwin.a.gz", - "src_binding_ptrcomp_sandbox_release_aarch64-apple-darwin.rs", - "rusty_v8_ptrcomp_sandbox_release_aarch64-apple-darwin.sha256", - }, - {path.name for path in Path(output_dir).iterdir()}, - ) - - def test_upstream_release_pair_paths(self) -> None: - self.assertEqual( - ( - Path( - "/tmp/rusty_v8/target/x86_64-apple-darwin/release/gn_out/obj/" - "librusty_v8.a" - ), - Path( - "/tmp/rusty_v8/target/x86_64-apple-darwin/release/gn_out/" - "src_binding.rs" - ), - ), - rusty_v8_bazel.upstream_release_pair_paths( - Path("/tmp/rusty_v8"), - "x86_64-apple-darwin", - ), - ) - self.assertEqual( - ( - Path( - "/tmp/rusty_v8/target/x86_64-pc-windows-msvc/release/gn_out/" - "obj/rusty_v8.lib" - ), - Path( - "/tmp/rusty_v8/target/x86_64-pc-windows-msvc/release/gn_out/" - "src_binding.rs" - ), - ), - rusty_v8_bazel.upstream_release_pair_paths( - Path("/tmp/rusty_v8"), - "x86_64-pc-windows-msvc", - ), - ) - - def test_stage_upstream_release_pair(self) -> None: - with TemporaryDirectory() as source_dir, TemporaryDirectory() as output_dir: - source_root = Path(source_dir) - gn_out = ( - source_root - / "target" - / "x86_64-pc-windows-msvc" - / "release" - / "gn_out" - ) - (gn_out / "obj").mkdir(parents=True) - (gn_out / "obj" / "rusty_v8.lib").write_bytes(b"archive") - (gn_out / "src_binding.rs").write_text("binding") - - rusty_v8_bazel.stage_upstream_release_pair( - source_root, - "x86_64-pc-windows-msvc", - Path(output_dir), - sandbox=True, - ) - - self.assertEqual( - { - "rusty_v8_ptrcomp_sandbox_release_x86_64-pc-windows-msvc.lib.gz", - "src_binding_ptrcomp_sandbox_release_x86_64-pc-windows-msvc.rs", - "rusty_v8_ptrcomp_sandbox_release_x86_64-pc-windows-msvc.sha256", - }, - {path.name for path in Path(output_dir).iterdir()}, - ) - - def test_ensure_bazel_output_files_rebuilds_existing_outputs(self) -> None: - with TemporaryDirectory() as output_dir: - output = Path(output_dir) / "libv8.a" - output.write_bytes(b"archive") - - with ( - patch.object(rusty_v8_bazel, "bazel_build") as bazel_build, - patch.object( - rusty_v8_bazel, - "bazel_output_files", - return_value=[output], - ) as bazel_output_files, - ): - self.assertEqual( - [output], - rusty_v8_bazel.ensure_bazel_output_files( - "macos_arm64", - ["//third_party/v8:pair"], - "opt", - ["rusty-v8-upstream-libcxx"], - ), - ) - - bazel_build.assert_called_once_with( - "macos_arm64", - ["//third_party/v8:pair"], - "opt", - ["rusty-v8-upstream-libcxx"], - download_toplevel=True, - ) - bazel_output_files.assert_called_once_with( - "macos_arm64", - ["//third_party/v8:pair"], - "opt", - ["rusty-v8-upstream-libcxx"], - ) - - def test_update_module_bazel_replaces_and_inserts_sha256(self) -> None: - module_bazel = textwrap.dedent( - """\ - http_file( - name = "rusty_v8_146_4_0_x86_64_unknown_linux_gnu_archive", - downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz", - sha256 = "0000000000000000000000000000000000000000000000000000000000000000", - urls = [ - "https://example.test/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz", - ], - ) - - http_file( - name = "rusty_v8_146_4_0_x86_64_unknown_linux_musl_binding", - downloaded_file_path = "src_binding_release_x86_64-unknown-linux-musl.rs", - urls = [ - "https://example.test/src_binding_release_x86_64-unknown-linux-musl.rs", - ], - ) - - http_file( - name = "rusty_v8_145_0_0_x86_64_unknown_linux_gnu_archive", - downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz", - sha256 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - urls = [ - "https://example.test/old.gz", - ], - ) - """ - ) - checksums = { - "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz": ( - "1111111111111111111111111111111111111111111111111111111111111111" - ), - "src_binding_release_x86_64-unknown-linux-musl.rs": ( - "2222222222222222222222222222222222222222222222222222222222222222" - ), - } - - updated = rusty_v8_module_bazel.update_module_bazel_text( - module_bazel, - checksums, - "146.4.0", - ) - - self.assertEqual( - textwrap.dedent( - """\ - http_file( - name = "rusty_v8_146_4_0_x86_64_unknown_linux_gnu_archive", - downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz", - sha256 = "1111111111111111111111111111111111111111111111111111111111111111", - urls = [ - "https://example.test/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz", - ], - ) - - http_file( - name = "rusty_v8_146_4_0_x86_64_unknown_linux_musl_binding", - downloaded_file_path = "src_binding_release_x86_64-unknown-linux-musl.rs", - sha256 = "2222222222222222222222222222222222222222222222222222222222222222", - urls = [ - "https://example.test/src_binding_release_x86_64-unknown-linux-musl.rs", - ], - ) - - http_file( - name = "rusty_v8_145_0_0_x86_64_unknown_linux_gnu_archive", - downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz", - sha256 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - urls = [ - "https://example.test/old.gz", - ], - ) - """ - ), - updated, - ) - rusty_v8_module_bazel.check_module_bazel_text(updated, checksums, "146.4.0") - - def test_check_module_bazel_rejects_manifest_drift(self) -> None: - module_bazel = textwrap.dedent( - """\ - http_file( - name = "rusty_v8_146_4_0_x86_64_unknown_linux_gnu_archive", - downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz", - sha256 = "1111111111111111111111111111111111111111111111111111111111111111", - urls = [ - "https://example.test/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz", - ], - ) - """ - ) - checksums = { - "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz": ( - "1111111111111111111111111111111111111111111111111111111111111111" - ), - "orphan.gz": ( - "2222222222222222222222222222222222222222222222222222222222222222" - ), - } - - with self.assertRaisesRegex( - rusty_v8_module_bazel.RustyV8ChecksumError, - "manifest has orphan.gz", - ): - rusty_v8_module_bazel.check_module_bazel_text( - module_bazel, - checksums, - "146.4.0", - ) - - def test_rusty_v8_http_file_versions(self) -> None: - module_bazel = textwrap.dedent( - """\ - http_file( - name = "rusty_v8_146_4_0_x86_64_unknown_linux_gnu_archive", - downloaded_file_path = "archive.gz", - urls = ["https://example.test/archive.gz"], - ) - - http_file( - name = "rusty_v8_147_4_0_x86_64_unknown_linux_gnu_archive", - downloaded_file_path = "new-archive.gz", - urls = ["https://example.test/new-archive.gz"], - ) - - http_file( - name = "unrelated_archive", - downloaded_file_path = "other.gz", - urls = ["https://example.test/other.gz"], - ) - """ - ) - - self.assertEqual( - ["146.4.0", "147.4.0"], - rusty_v8_module_bazel.rusty_v8_http_file_versions(module_bazel), - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/reference/openai-codex/.github/scripts/verify_bazel_clippy_lints.py b/reference/openai-codex/.github/scripts/verify_bazel_clippy_lints.py deleted file mode 100644 index 6b27973..0000000 --- a/reference/openai-codex/.github/scripts/verify_bazel_clippy_lints.py +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env python3 - -from __future__ import annotations - -import argparse -import re -import sys -import tomllib -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[2] -DEFAULT_CARGO_TOML = ROOT / "codex-rs" / "Cargo.toml" -DEFAULT_BAZELRC = ROOT / ".bazelrc" -BAZEL_CLIPPY_FLAG_PREFIX = "build:clippy --@rules_rust//rust/settings:clippy_flag=" -BAZEL_SPECIAL_FLAGS = {"-Dwarnings"} -VALID_LEVELS = {"allow", "warn", "deny", "forbid"} -LONG_FLAG_RE = re.compile( - r"^--(?Pallow|warn|deny|forbid)=clippy::(?P[a-z0-9_]+)$" -) -SHORT_FLAG_RE = re.compile(r"^-(?P[AWDF])clippy::(?P[a-z0-9_]+)$") -SHORT_LEVEL_NAMES = { - "A": "allow", - "W": "warn", - "D": "deny", - "F": "forbid", -} - - -def main() -> int: - parser = argparse.ArgumentParser( - description=( - "Verify that Bazel clippy flags in .bazelrc stay in sync with " - "codex-rs/Cargo.toml [workspace.lints.clippy]." - ) - ) - parser.add_argument( - "--cargo-toml", - type=Path, - default=DEFAULT_CARGO_TOML, - help="Path to the workspace Cargo.toml to inspect.", - ) - parser.add_argument( - "--bazelrc", - type=Path, - default=DEFAULT_BAZELRC, - help="Path to the .bazelrc file to inspect.", - ) - args = parser.parse_args() - - cargo_toml = args.cargo_toml.resolve() - bazelrc = args.bazelrc.resolve() - - cargo_lints = load_workspace_clippy_lints(cargo_toml) - bazel_lints = load_bazel_clippy_lints(bazelrc) - - missing = sorted(cargo_lints.keys() - bazel_lints.keys()) - extra = sorted(bazel_lints.keys() - cargo_lints.keys()) - mismatched = sorted( - lint - for lint in cargo_lints.keys() & bazel_lints.keys() - if cargo_lints[lint] != bazel_lints[lint] - ) - - if missing or extra or mismatched: - print_sync_error( - cargo_toml=cargo_toml, - bazelrc=bazelrc, - cargo_lints=cargo_lints, - bazel_lints=bazel_lints, - missing=missing, - extra=extra, - mismatched=mismatched, - ) - return 1 - - print( - "Bazel clippy flags in " - f"{display_path(bazelrc)} match " - f"{display_path(cargo_toml)} [workspace.lints.clippy]." - ) - return 0 - - -def load_workspace_clippy_lints(cargo_toml: Path) -> dict[str, str]: - workspace = tomllib.loads(cargo_toml.read_text())["workspace"] - clippy_lints = workspace["lints"]["clippy"] - parsed: dict[str, str] = {} - for lint, level in clippy_lints.items(): - if not isinstance(level, str): - raise SystemExit( - f"expected string lint level for clippy::{lint} in {cargo_toml}, got {level!r}" - ) - normalized = level.strip().lower() - if normalized not in VALID_LEVELS: - raise SystemExit( - f"unsupported lint level {level!r} for clippy::{lint} in {cargo_toml}" - ) - parsed[lint] = normalized - return parsed - - -def load_bazel_clippy_lints(bazelrc: Path) -> dict[str, str]: - parsed: dict[str, str] = {} - line_numbers: dict[str, int] = {} - - for lineno, line in enumerate(bazelrc.read_text().splitlines(), start=1): - if not line.startswith(BAZEL_CLIPPY_FLAG_PREFIX): - continue - - flag = line.removeprefix(BAZEL_CLIPPY_FLAG_PREFIX).strip() - if flag in BAZEL_SPECIAL_FLAGS: - continue - - parsed_flag = parse_bazel_lint_flag(flag) - if parsed_flag is None: - continue - - lint, level = parsed_flag - if lint in parsed: - raise SystemExit( - f"duplicate Bazel clippy entry for clippy::{lint} at " - f"{bazelrc}:{line_numbers[lint]} and {bazelrc}:{lineno}" - ) - parsed[lint] = level - line_numbers[lint] = lineno - - return parsed - - -def parse_bazel_lint_flag(flag: str) -> tuple[str, str] | None: - long_match = LONG_FLAG_RE.match(flag) - if long_match: - return long_match["lint"], long_match["level"] - - short_match = SHORT_FLAG_RE.match(flag) - if short_match: - return short_match["lint"], SHORT_LEVEL_NAMES[short_match["level"]] - - return None - - -def print_sync_error( - *, - cargo_toml: Path, - bazelrc: Path, - cargo_lints: dict[str, str], - bazel_lints: dict[str, str], - missing: list[str], - extra: list[str], - mismatched: list[str], -) -> None: - cargo_toml_display = display_path(cargo_toml) - bazelrc_display = display_path(bazelrc) - example_manifest = find_workspace_lints_example_manifest() - - print( - "ERROR: Bazel clippy flags are out of sync with Cargo workspace clippy lints.", - file=sys.stderr, - ) - print(file=sys.stderr) - print( - f"Cargo defines the source of truth in {cargo_toml_display} " - "[workspace.lints.clippy].", - file=sys.stderr, - ) - if example_manifest is not None: - print( - "Cargo applies those lint levels to member crates that opt into " - f"`[lints] workspace = true`, for example {example_manifest}.", - file=sys.stderr, - ) - print( - "Bazel clippy does not ingest Cargo lint levels automatically, and " - "`clippy.toml` can configure lint behavior but cannot set allow/warn/deny/forbid.", - file=sys.stderr, - ) - print( - f"Update {bazelrc_display} so its `build:clippy` " - "`clippy_flag` entries match Cargo.", - file=sys.stderr, - ) - - if missing: - print(file=sys.stderr) - print("Missing Bazel entries:", file=sys.stderr) - for lint in missing: - print(f" {render_bazelrc_line(lint, cargo_lints[lint])}", file=sys.stderr) - - if mismatched: - print(file=sys.stderr) - print("Mismatched lint levels:", file=sys.stderr) - for lint in mismatched: - cargo_level = cargo_lints[lint] - bazel_level = bazel_lints[lint] - print( - f" clippy::{lint}: Cargo has {cargo_level}, Bazel has {bazel_level}", - file=sys.stderr, - ) - print( - f" expected: {render_bazelrc_line(lint, cargo_level)}", - file=sys.stderr, - ) - - if extra: - print(file=sys.stderr) - print("Extra Bazel entries with no Cargo counterpart:", file=sys.stderr) - for lint in extra: - print(f" {render_bazelrc_line(lint, bazel_lints[lint])}", file=sys.stderr) - - -def render_bazelrc_line(lint: str, level: str) -> str: - return f"{BAZEL_CLIPPY_FLAG_PREFIX}--{level}=clippy::{lint}" - - -def display_path(path: Path) -> str: - try: - return str(path.relative_to(ROOT)) - except ValueError: - return str(path) - - -def find_workspace_lints_example_manifest() -> str | None: - for cargo_toml in sorted((ROOT / "codex-rs").glob("**/Cargo.toml")): - if cargo_toml == DEFAULT_CARGO_TOML: - continue - data = tomllib.loads(cargo_toml.read_text()) - if data.get("lints", {}).get("workspace") is True: - return str(cargo_toml.relative_to(ROOT)) - return None - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/reference/openai-codex/.github/scripts/verify_cargo_workspace_manifests.py b/reference/openai-codex/.github/scripts/verify_cargo_workspace_manifests.py deleted file mode 100644 index 93b41ea..0000000 --- a/reference/openai-codex/.github/scripts/verify_cargo_workspace_manifests.py +++ /dev/null @@ -1,391 +0,0 @@ -#!/usr/bin/env python3 - -"""Verify that codex-rs Cargo manifests follow workspace manifest policy. - -Checks: -- Crates inherit `[workspace.package]` metadata. -- Crates opt into `[lints] workspace = true`. -- Crate names follow the codex-rs directory naming conventions. -- Workspace manifests do not introduce workspace crate feature toggles. -""" - -from __future__ import annotations - -import sys -import tomllib -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[2] -CARGO_RS_ROOT = ROOT / "codex-rs" -WORKSPACE_PACKAGE_FIELDS = ("version", "edition", "license") -TOP_LEVEL_NAME_EXCEPTIONS = { - "windows-sandbox-rs": "codex-windows-sandbox", -} -UTILITY_NAME_EXCEPTIONS = { - "path-utils": "codex-utils-path", -} -MANIFEST_FEATURE_EXCEPTIONS = { - "codex-rs/code-mode/Cargo.toml": {"sandbox": ("v8/v8_enable_sandbox",)}, - "codex-rs/v8-poc/Cargo.toml": {"sandbox": ("v8/v8_enable_sandbox",)}, -} -OPTIONAL_DEPENDENCY_EXCEPTIONS = set() -INTERNAL_DEPENDENCY_FEATURE_EXCEPTIONS = {} - - -def main() -> int: - internal_package_names = workspace_package_names() - used_manifest_feature_exceptions: set[str] = set() - used_optional_dependency_exceptions: set[tuple[str, str, str]] = set() - used_internal_dependency_feature_exceptions: set[tuple[str, str, str]] = set() - failures_by_path: dict[str, list[str]] = {} - - for path in manifests_to_verify(): - if errors := manifest_errors( - path, - internal_package_names, - used_manifest_feature_exceptions, - used_optional_dependency_exceptions, - used_internal_dependency_feature_exceptions, - ): - failures_by_path[manifest_key(path)] = errors - - add_unused_exception_errors( - failures_by_path, - used_manifest_feature_exceptions, - used_optional_dependency_exceptions, - used_internal_dependency_feature_exceptions, - ) - - if not failures_by_path: - return 0 - - print( - "Cargo manifests under codex-rs must inherit workspace package metadata, " - "opt into workspace lints, and avoid introducing new workspace crate " - "features." - ) - print( - "Workspace crate features are disallowed because our Bazel build setup " - "does not honor them today, which can let issues hidden behind feature " - "gates go unnoticed, and because they add extra crate build " - "permutations we want to avoid." - ) - print( - "Cargo only applies `codex-rs/Cargo.toml` `[workspace.lints.clippy]` " - "entries to a crate when that crate declares:" - ) - print() - print("[lints]") - print("workspace = true") - print() - print( - "Without that opt-in, `cargo clippy` can miss violations that Bazel clippy " - "catches." - ) - print() - print( - "Package-name checks apply to `codex-rs//Cargo.toml` and " - "`codex-rs/utils//Cargo.toml`." - ) - print( - "Workspace crate features are forbidden; add a targeted exception here " - "only if there is a deliberate temporary migration in flight." - ) - print() - for path in sorted(failures_by_path): - errors = failures_by_path[path] - print(f"{path}:") - for error in errors: - print(f" - {error}") - - return 1 - - -def manifest_errors( - path: Path, - internal_package_names: set[str], - used_manifest_feature_exceptions: set[str], - used_optional_dependency_exceptions: set[tuple[str, str, str]], - used_internal_dependency_feature_exceptions: set[tuple[str, str, str]], -) -> list[str]: - manifest = load_manifest(path) - package = manifest.get("package") - if not isinstance(package, dict) and path != CARGO_RS_ROOT / "Cargo.toml": - return [] - - errors = [] - if isinstance(package, dict): - for field in WORKSPACE_PACKAGE_FIELDS: - if not is_workspace_reference(package.get(field)): - errors.append(f"set `{field}.workspace = true` in `[package]`") - - lints = manifest.get("lints") - if not (isinstance(lints, dict) and lints.get("workspace") is True): - errors.append("add `[lints]` with `workspace = true`") - - expected_name = expected_package_name(path) - if expected_name is not None: - actual_name = package.get("name") - if actual_name != expected_name: - errors.append( - f"set `[package].name` to `{expected_name}` (found `{actual_name}`)" - ) - - path_key = manifest_key(path) - features = manifest.get("features") - if features is not None: - normalized_features = normalize_feature_mapping(features) - expected_features = MANIFEST_FEATURE_EXCEPTIONS.get(path_key) - if expected_features is None: - errors.append( - "remove `[features]`; new workspace crate features are not allowed" - ) - else: - used_manifest_feature_exceptions.add(path_key) - if normalized_features != expected_features: - errors.append( - "limit `[features]` to the existing exception list while " - "workspace crate features are being removed " - f"(expected {render_feature_mapping(expected_features)})" - ) - - for section_name, dependencies in dependency_sections(manifest): - for dependency_name, dependency in dependencies.items(): - if not isinstance(dependency, dict): - continue - - if dependency.get("optional") is True: - exception_key = (path_key, section_name, dependency_name) - if exception_key in OPTIONAL_DEPENDENCY_EXCEPTIONS: - used_optional_dependency_exceptions.add(exception_key) - else: - errors.append( - "remove `optional = true` from " - f"`{dependency_entry_label(section_name, dependency_name)}`; " - "new optional dependencies are not allowed because they " - "create crate features" - ) - - if not is_internal_dependency(path, dependency_name, dependency, internal_package_names): - continue - - dependency_features = dependency.get("features") - if dependency_features is not None: - normalized_dependency_features = normalize_string_list( - dependency_features - ) - exception_key = (path_key, section_name, dependency_name) - expected_dependency_features = ( - INTERNAL_DEPENDENCY_FEATURE_EXCEPTIONS.get(exception_key) - ) - if expected_dependency_features is None: - errors.append( - "remove `features = [...]` from workspace dependency " - f"`{dependency_entry_label(section_name, dependency_name)}`; " - "new workspace crate feature activations are not allowed" - ) - else: - used_internal_dependency_feature_exceptions.add(exception_key) - if normalized_dependency_features != expected_dependency_features: - errors.append( - "limit workspace dependency features on " - f"`{dependency_entry_label(section_name, dependency_name)}` " - "to the existing exception list while workspace crate " - "features are being removed " - f"(expected {render_string_list(expected_dependency_features)})" - ) - - if dependency.get("default-features") is False: - errors.append( - "remove `default-features = false` from workspace dependency " - f"`{dependency_entry_label(section_name, dependency_name)}`; " - "new workspace crate feature toggles are not allowed" - ) - - return errors - - -def expected_package_name(path: Path) -> str | None: - parts = path.relative_to(CARGO_RS_ROOT).parts - if len(parts) == 2 and parts[1] == "Cargo.toml": - directory = parts[0] - return TOP_LEVEL_NAME_EXCEPTIONS.get( - directory, - directory if directory.startswith("codex-") else f"codex-{directory}", - ) - if len(parts) == 3 and parts[0] == "utils" and parts[2] == "Cargo.toml": - directory = parts[1] - return UTILITY_NAME_EXCEPTIONS.get(directory, f"codex-utils-{directory}") - return None - - -def is_workspace_reference(value: object) -> bool: - return isinstance(value, dict) and value.get("workspace") is True - - -def manifest_key(path: Path) -> str: - return str(path.relative_to(ROOT)) - - -def normalize_feature_mapping(value: object) -> dict[str, tuple[str, ...]] | None: - if not isinstance(value, dict): - return None - - normalized = {} - for key, features in value.items(): - if not isinstance(key, str): - return None - normalized_features = normalize_string_list(features) - if normalized_features is None: - return None - normalized[key] = normalized_features - return normalized - - -def normalize_string_list(value: object) -> tuple[str, ...] | None: - if not isinstance(value, list) or not all(isinstance(item, str) for item in value): - return None - return tuple(value) - - -def render_feature_mapping(features: dict[str, tuple[str, ...]]) -> str: - entries = [ - f"{name} = {render_string_list(items)}" for name, items in features.items() - ] - return ", ".join(entries) - - -def render_string_list(items: tuple[str, ...]) -> str: - return "[" + ", ".join(f'"{item}"' for item in items) + "]" - - -def dependency_sections(manifest: dict) -> list[tuple[str, dict]]: - sections = [] - for section_name in ("dependencies", "dev-dependencies", "build-dependencies"): - dependencies = manifest.get(section_name) - if isinstance(dependencies, dict): - sections.append((section_name, dependencies)) - - workspace = manifest.get("workspace") - if isinstance(workspace, dict): - workspace_dependencies = workspace.get("dependencies") - if isinstance(workspace_dependencies, dict): - sections.append(("workspace.dependencies", workspace_dependencies)) - - target = manifest.get("target") - if not isinstance(target, dict): - return sections - - for target_name, tables in target.items(): - if not isinstance(tables, dict): - continue - for section_name in ("dependencies", "dev-dependencies", "build-dependencies"): - dependencies = tables.get(section_name) - if isinstance(dependencies, dict): - sections.append((f"target.{target_name}.{section_name}", dependencies)) - - return sections - - -def dependency_entry_label(section_name: str, dependency_name: str) -> str: - return f"[{section_name}].{dependency_name}" - - -def is_internal_dependency( - manifest_path: Path, - dependency_name: str, - dependency: dict, - internal_package_names: set[str], -) -> bool: - package_name = dependency.get("package", dependency_name) - if isinstance(package_name, str) and package_name in internal_package_names: - return True - - dependency_path = dependency.get("path") - if not isinstance(dependency_path, str): - return False - - resolved_dependency_path = (manifest_path.parent / dependency_path).resolve() - try: - resolved_dependency_path.relative_to(CARGO_RS_ROOT) - except ValueError: - return False - return True - - -def add_unused_exception_errors( - failures_by_path: dict[str, list[str]], - used_manifest_feature_exceptions: set[str], - used_optional_dependency_exceptions: set[tuple[str, str, str]], - used_internal_dependency_feature_exceptions: set[tuple[str, str, str]], -) -> None: - for path_key in sorted( - set(MANIFEST_FEATURE_EXCEPTIONS) - used_manifest_feature_exceptions - ): - add_failure( - failures_by_path, - path_key, - "remove the stale `[features]` exception from " - "`MANIFEST_FEATURE_EXCEPTIONS`", - ) - - for path_key, section_name, dependency_name in sorted( - OPTIONAL_DEPENDENCY_EXCEPTIONS - used_optional_dependency_exceptions - ): - add_failure( - failures_by_path, - path_key, - "remove the stale optional-dependency exception for " - f"`{dependency_entry_label(section_name, dependency_name)}` from " - "`OPTIONAL_DEPENDENCY_EXCEPTIONS`", - ) - - for path_key, section_name, dependency_name in sorted( - set(INTERNAL_DEPENDENCY_FEATURE_EXCEPTIONS) - - used_internal_dependency_feature_exceptions - ): - add_failure( - failures_by_path, - path_key, - "remove the stale internal dependency feature exception for " - f"`{dependency_entry_label(section_name, dependency_name)}` from " - "`INTERNAL_DEPENDENCY_FEATURE_EXCEPTIONS`", - ) - - -def add_failure(failures_by_path: dict[str, list[str]], path_key: str, error: str) -> None: - failures_by_path.setdefault(path_key, []).append(error) - - -def workspace_package_names() -> set[str]: - package_names = set() - for path in cargo_manifests(): - manifest = load_manifest(path) - package = manifest.get("package") - if not isinstance(package, dict): - continue - package_name = package.get("name") - if isinstance(package_name, str): - package_names.add(package_name) - return package_names - - -def load_manifest(path: Path) -> dict: - return tomllib.loads(path.read_text()) - - -def cargo_manifests() -> list[Path]: - return sorted( - path - for path in CARGO_RS_ROOT.rglob("Cargo.toml") - if path != CARGO_RS_ROOT / "Cargo.toml" - ) - - -def manifests_to_verify() -> list[Path]: - return [CARGO_RS_ROOT / "Cargo.toml", *cargo_manifests()] - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/reference/openai-codex/.github/scripts/verify_tui_core_boundary.py b/reference/openai-codex/.github/scripts/verify_tui_core_boundary.py deleted file mode 100644 index e66afec..0000000 --- a/reference/openai-codex/.github/scripts/verify_tui_core_boundary.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python3 - -"""Verify codex-tui does not depend on or import codex-core directly.""" - -from __future__ import annotations - -import re -import sys -import tomllib -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[2] -TUI_ROOT = ROOT / "codex-rs" / "tui" -TUI_MANIFEST = TUI_ROOT / "Cargo.toml" -FORBIDDEN_PACKAGE = "codex-core" -FORBIDDEN_SOURCE_PATTERNS = ( - re.compile(r"\bcodex_core::"), - re.compile(r"\buse\s+codex_core\b"), - re.compile(r"\bextern\s+crate\s+codex_core\b"), -) - - -def main() -> int: - failures = [] - failures.extend(manifest_failures()) - failures.extend(source_failures()) - - if not failures: - return 0 - - print("codex-tui must not depend on or import codex-core directly.") - print( - "Use the app-server protocol/client boundary instead; temporary embedded " - "startup gaps belong behind codex_app_server_client::legacy_core." - ) - print() - for failure in failures: - print(f"- {failure}") - - return 1 - - -def manifest_failures() -> list[str]: - manifest = tomllib.loads(TUI_MANIFEST.read_text()) - failures = [] - for section_name, dependencies in dependency_sections(manifest): - if FORBIDDEN_PACKAGE in dependencies: - failures.append( - f"{relative_path(TUI_MANIFEST)} declares `{FORBIDDEN_PACKAGE}` " - f"in `[{section_name}]`" - ) - return failures - - -def dependency_sections(manifest: dict) -> list[tuple[str, dict]]: - sections: list[tuple[str, dict]] = [] - for section_name in ("dependencies", "dev-dependencies", "build-dependencies"): - dependencies = manifest.get(section_name) - if isinstance(dependencies, dict): - sections.append((section_name, dependencies)) - - for target_name, target in manifest.get("target", {}).items(): - if not isinstance(target, dict): - continue - for section_name in ("dependencies", "dev-dependencies", "build-dependencies"): - dependencies = target.get(section_name) - if isinstance(dependencies, dict): - sections.append((f'target.{target_name}.{section_name}', dependencies)) - - return sections - - -def source_failures() -> list[str]: - failures = [] - for path in sorted(TUI_ROOT.glob("**/*.rs")): - text = path.read_text() - for line_number, line in enumerate(text.splitlines(), start=1): - if any(pattern.search(line) for pattern in FORBIDDEN_SOURCE_PATTERNS): - failures.append(f"{relative_path(path)}:{line_number} imports `codex_core`") - return failures - - -def relative_path(path: Path) -> str: - return str(path.relative_to(ROOT)) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/reference/openai-codex/.github/workflows/Dockerfile.bazel b/reference/openai-codex/.github/workflows/Dockerfile.bazel deleted file mode 100644 index 51c199d..0000000 --- a/reference/openai-codex/.github/workflows/Dockerfile.bazel +++ /dev/null @@ -1,20 +0,0 @@ -FROM ubuntu:24.04 - -# TODO(mbolin): Published to docker.io/mbolin491/codex-bazel:latest for -# initial debugging, but we should publish to a more proper location. -# -# docker buildx create --use -# docker buildx build --platform linux/amd64,linux/arm64 -f .github/workflows/Dockerfile.bazel -t mbolin491/codex-bazel:latest --push . - -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - curl git python3 ca-certificates && \ - rm -rf /var/lib/apt/lists/* - -# Install dotslash. -RUN curl -LSfs "https://github.com/facebook/dotslash/releases/download/v0.5.8/dotslash-ubuntu-22.04.$(uname -m).tar.gz" | tar fxz - -C /usr/local/bin - -# Ubuntu 24.04 ships with user 'ubuntu' already created with UID 1000. -USER ubuntu - -WORKDIR /workspace diff --git a/reference/openai-codex/.github/workflows/README.md b/reference/openai-codex/.github/workflows/README.md deleted file mode 100644 index b2403b7..0000000 --- a/reference/openai-codex/.github/workflows/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Workflow Strategy - -The workflows in this directory are split so that pull requests get fast, review-friendly signal while `main` still gets the full cross-platform verification pass. - -## Pull Requests - -- `bazel.yml` is the main pre-merge verification path for Rust code. - It runs Bazel `test` and Bazel `clippy` on the supported Bazel targets, - including the generated Rust test binaries needed to lint inline `#[cfg(test)]` - code. -- `rust-ci.yml` keeps the Cargo-native PR checks intentionally small: - - `cargo fmt --check` - - `cargo shear` - - `argument-comment-lint` on Linux, macOS, and Windows - - `tools/argument-comment-lint` package tests when the lint or its workflow wiring changes - -## Post-Merge On `main` - -- `bazel.yml` also runs on pushes to `main`. - This re-verifies the merged Bazel path and helps keep the BuildBuddy caches warm. -- `rust-ci-full.yml` is the full Cargo-native verification workflow. - It keeps the heavier checks off the PR path while still validating them after merge: - - the full Cargo `clippy` matrix - - the full Cargo `nextest` matrix via per-platform archive-backed shards - - Windows ARM64 nextest archives cross-compiled on Windows x64, then replayed on native Windows ARM64 shards - - release-profile Cargo builds - - cross-platform `argument-comment-lint` - - Linux remote-env tests - -## Rule Of Thumb - -- If a build/test/clippy check can be expressed in Bazel, prefer putting the PR-time version in `bazel.yml`. -- Keep `rust-ci.yml` fast enough that it usually does not dominate PR latency. -- Reserve `rust-ci-full.yml` for heavyweight Cargo-native coverage that Bazel does not replace yet. diff --git a/reference/openai-codex/.github/workflows/bazel.yml b/reference/openai-codex/.github/workflows/bazel.yml deleted file mode 100644 index 11c0988..0000000 --- a/reference/openai-codex/.github/workflows/bazel.yml +++ /dev/null @@ -1,518 +0,0 @@ -name: Bazel - -# Note this workflow was originally derived from: -# https://github.com/cerisier/toolchains_llvm_bootstrapped/blob/main/.github/workflows/ci.yaml - -on: - pull_request: {} - push: - branches: - - main - workflow_dispatch: - -concurrency: - # Cancel previous actions from the same PR or branch except 'main' branch. - # See https://docs.github.com/en/actions/using-jobs/using-concurrency and https://docs.github.com/en/actions/learn-github-actions/contexts for more info. - group: concurrency-group::${{ github.workflow }}::${{ github.event.pull_request.number > 0 && format('pr-{0}', github.event.pull_request.number) || github.ref_name }}${{ github.ref_name == 'main' && format('::{0}', github.run_id) || ''}} - cancel-in-progress: ${{ github.ref_name != 'main' }} -jobs: - test: - # PRs use the sharded Windows cross-compiled test jobs below. Post-merge - # pushes to main also run the native Windows test job for broader Windows - # signal without putting PR latency back on the critical path. Cargo CI - # owns V8/code-mode test coverage for now. - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - include: - # macOS - - os: macos-15-xlarge - target: aarch64-apple-darwin - - os: macos-15-xlarge - target: x86_64-apple-darwin - - # Linux - - os: ubuntu-24.04 - target: x86_64-unknown-linux-gnu - - os: ubuntu-24.04 - target: x86_64-unknown-linux-musl - # 2026-02-27 Bazel tests have been flaky on arm in CI. - # Disable until we can investigate and stabilize them. - # - os: ubuntu-24.04-arm - # target: aarch64-unknown-linux-musl - # - os: ubuntu-24.04-arm - # target: aarch64-unknown-linux-gnu - - runs-on: ${{ matrix.os }} - - # Configure a human readable name for each job - name: Bazel test on ${{ matrix.os }} for ${{ matrix.target }} - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Check rusty_v8 MODULE.bazel checksums - if: matrix.os == 'ubuntu-24.04' && matrix.target == 'x86_64-unknown-linux-gnu' - shell: bash - run: | - python3 .github/scripts/rusty_v8_bazel.py check-module-bazel - python3 -m unittest discover -s .github/scripts -p test_rusty_v8_bazel.py - - - name: Prepare Bazel CI - id: prepare_bazel - uses: ./.github/actions/prepare-bazel-ci - with: - target: ${{ matrix.target }} - cache-scope: bazel-${{ github.job }} - install-test-prereqs: "true" - - name: Check MODULE.bazel.lock is up to date - if: matrix.os == 'ubuntu-24.04' && matrix.target == 'x86_64-unknown-linux-gnu' - shell: bash - run: ./scripts/check-module-bazel-lock.sh - - - name: bazel test //... - env: - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - shell: bash - run: | - bazel_targets=( - //... - # Keep standalone V8 library targets out of the ordinary Bazel CI - # path. V8 consumers under `//codex-rs/...` still participate - # transitively through `//...`. - -//third_party/v8:all - # V8-backed code-mode tests are covered by Cargo CI. Bazel CI - # cross-compiles in several legs, and those tests are not stable in - # that setup yet. - -//codex-rs/code-mode:code-mode-unit-tests - -//codex-rs/v8-poc:v8-poc-unit-tests - ) - - bazel_wrapper_args=( - --print-failed-action-summary - --print-failed-test-logs - ) - bazel_test_args=( - test - --test_tag_filters=-argument-comment-lint - --test_verbose_timeout_warnings - --build_metadata=COMMIT_SHA=${GITHUB_SHA} - ) - ./.github/scripts/run-bazel-ci.sh \ - "${bazel_wrapper_args[@]}" \ - -- \ - "${bazel_test_args[@]}" \ - -- \ - "${bazel_targets[@]}" - - - name: Upload Bazel execution logs - if: always() && !cancelled() - continue-on-error: true - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: bazel-execution-logs-test-${{ matrix.target }} - path: ${{ runner.temp }}/bazel-execution-logs - if-no-files-found: ignore - - # Save the job-scoped Bazel repository cache after cache misses. Keep the - # upload non-fatal so cache service issues never fail the job itself. - - name: Save bazel repository cache - if: always() && !cancelled() && steps.prepare_bazel.outputs.repository-cache-hit != 'true' - continue-on-error: true - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} - key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} - - test-windows-shard: - # Split the Windows Bazel test leg across separate Windows - # hosts. Each shard still uses Linux RBE for build actions, but the test - # execution itself happens on its own Windows runner. - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - shard: - - 1 - - 2 - - 3 - - 4 - runs-on: windows-latest - name: Bazel test on windows-latest for x86_64-pc-windows-gnullvm shard ${{ matrix.shard }}/4 - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Prepare Bazel CI - id: prepare_bazel - uses: ./.github/actions/prepare-bazel-ci - with: - target: x86_64-pc-windows-gnullvm - # Reuse the former monolithic Windows test cache for restores. Do - # not save it from every shard below; duplicate uploads would sit on - # the PR-blocking critical path after the useful test work is done. - cache-scope: bazel-test - install-test-prereqs: "true" - - - name: bazel test shard - env: - BAZEL_TEST_SHARD: ${{ matrix.shard }} - BAZEL_TEST_SHARD_COUNT: 4 - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - shell: bash - run: | - set -euo pipefail - - bazel_test_query='tests(//...) except tests(//third_party/v8:all) except //codex-rs/code-mode:code-mode-unit-tests except //codex-rs/v8-poc:v8-poc-unit-tests except attr(tags, "manual", tests(//...))' - mapfile -t bazel_targets < <( - MSYS2_ARG_CONV_EXCL='*' bazel query --output=label "${bazel_test_query}" \ - | LC_ALL=C sort - ) - - selected_targets=() - for bazel_target in "${bazel_targets[@]}"; do - target_bucket="$( - printf '%s\n' "${bazel_target}" \ - | cksum \ - | awk -v shard_count="${BAZEL_TEST_SHARD_COUNT}" '{ print ($1 % shard_count) + 1 }' - )" - if [[ "${target_bucket}" == "${BAZEL_TEST_SHARD}" ]]; then - selected_targets+=("${bazel_target}") - fi - done - - if [[ ${#selected_targets[@]} -eq 0 ]]; then - echo "No Bazel test targets selected for Windows shard ${BAZEL_TEST_SHARD}/${BAZEL_TEST_SHARD_COUNT}." >&2 - exit 1 - fi - - echo "Selected ${#selected_targets[@]} of ${#bazel_targets[@]} Bazel test targets for Windows shard ${BAZEL_TEST_SHARD}/${BAZEL_TEST_SHARD_COUNT}." - - bazel_test_args=( - test - --skip_incompatible_explicit_targets - --test_tag_filters=-argument-comment-lint - --test_verbose_timeout_warnings - --build_metadata=COMMIT_SHA=${GITHUB_SHA} - --build_metadata=TAG_windows_test_shard=${BAZEL_TEST_SHARD} - ) - - ./.github/scripts/run-bazel-ci.sh \ - --print-failed-action-summary \ - --print-failed-test-logs \ - --windows-cross-compile \ - --remote-download-toplevel \ - -- \ - "${bazel_test_args[@]}" \ - -- \ - "${selected_targets[@]}" - - - name: Upload Bazel execution logs - if: always() && !cancelled() - continue-on-error: true - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: bazel-execution-logs-test-x86_64-pc-windows-gnullvm-shard-${{ matrix.shard }} - path: ${{ runner.temp }}/bazel-execution-logs - if-no-files-found: ignore - - test-windows: - # Preserve the existing required-check surface while the real work happens - # in the sharded Windows jobs above. - if: always() - needs: test-windows-shard - runs-on: ubuntu-24.04 - name: Bazel test on windows-latest for x86_64-pc-windows-gnullvm - - steps: - - name: Confirm Windows Bazel test shards passed - shell: bash - run: | - if [[ "${{ needs.test-windows-shard.result }}" != "success" ]]; then - echo "Windows Bazel test shards finished with result: ${{ needs.test-windows-shard.result }}" >&2 - exit 1 - fi - - test-windows-native-main: - # Native Windows Bazel tests are slower and frequently approach the - # 30-minute PR budget. Run this only for post-merge commits to main and give - # it a larger timeout. - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - timeout-minutes: 40 - runs-on: windows-latest - name: Bazel test on windows-latest for x86_64-pc-windows-gnullvm (native main) - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Prepare Bazel CI - id: prepare_bazel - uses: ./.github/actions/prepare-bazel-ci - with: - target: x86_64-pc-windows-gnullvm - cache-scope: bazel-${{ github.job }} - install-test-prereqs: "true" - - - name: bazel test //... - env: - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - shell: bash - run: | - bazel_targets=( - //... - # Keep standalone V8 library targets out of the ordinary Bazel CI - # path. V8 consumers under `//codex-rs/...` still participate - # transitively through `//...`. - -//third_party/v8:all - # Keep this aligned with the main Bazel job. The native Windows - # job preserves broad post-merge coverage, but code-mode/V8 tests - # are covered by Cargo CI rather than Bazel for now. - -//codex-rs/code-mode:code-mode-unit-tests - -//codex-rs/v8-poc:v8-poc-unit-tests - ) - - bazel_test_args=( - test - --test_tag_filters=-argument-comment-lint - --test_verbose_timeout_warnings - --build_metadata=COMMIT_SHA=${GITHUB_SHA} - --build_metadata=TAG_windows_native_main=true - ) - - ./.github/scripts/run-bazel-ci.sh \ - --print-failed-action-summary \ - --print-failed-test-logs \ - -- \ - "${bazel_test_args[@]}" \ - -- \ - "${bazel_targets[@]}" - - - name: Upload Bazel execution logs - if: always() && !cancelled() - continue-on-error: true - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: bazel-execution-logs-test-windows-native-x86_64-pc-windows-gnullvm - path: ${{ runner.temp }}/bazel-execution-logs - if-no-files-found: ignore - - # Save the job-scoped Bazel repository cache after cache misses. Keep the - # upload non-fatal so cache service issues never fail the job itself. - - name: Save bazel repository cache - if: always() && !cancelled() && steps.prepare_bazel.outputs.repository-cache-hit != 'true' - continue-on-error: true - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} - key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} - - clippy: - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - include: - # Keep Linux lint coverage on x64 and add the arm64 macOS path that - # the Bazel test job already exercises. Add Windows gnullvm as well - # so PRs get Bazel-native lint signal on the same Windows toolchain - # that the Bazel test job uses. - - os: ubuntu-24.04 - target: x86_64-unknown-linux-gnu - - os: macos-15-xlarge - target: aarch64-apple-darwin - - os: windows-latest - target: x86_64-pc-windows-gnullvm - runs-on: ${{ matrix.os }} - name: Bazel clippy on ${{ matrix.os }} for ${{ matrix.target }} - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Prepare Bazel CI - id: prepare_bazel - uses: ./.github/actions/prepare-bazel-ci - with: - target: ${{ matrix.target }} - cache-scope: bazel-${{ github.job }} - - - name: bazel build --config=clippy lint targets - env: - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - shell: bash - run: | - bazel_clippy_args=( - --config=clippy - --build_metadata=COMMIT_SHA=${GITHUB_SHA} - --build_metadata=TAG_job=clippy - ) - bazel_wrapper_args=() - bazel_target_list_args=() - if [[ "${RUNNER_OS}" == "Windows" ]]; then - # Keep this aligned with the fast Windows Bazel test job: use - # Linux RBE for clippy build actions while targeting Windows - # gnullvm. Fork/community PRs without the BuildBuddy secret fall - # back inside `run-bazel-ci.sh` to the previous local Windows MSVC - # host-platform shape. - bazel_wrapper_args+=(--windows-cross-compile) - bazel_target_list_args+=(--windows-cross-compile) - if [[ -z "${BUILDBUDDY_API_KEY:-}" ]]; then - # The fork fallback can see incompatible explicit Windows-cross - # internal test binaries in the generated target list. Preserve - # the old local-fallback behavior there. - bazel_clippy_args+=(--skip_incompatible_explicit_targets) - fi - fi - - bazel_target_lines="$(./scripts/list-bazel-clippy-targets.sh "${bazel_target_list_args[@]}")" - bazel_targets=() - while IFS= read -r target; do - bazel_targets+=("${target}") - done <<< "${bazel_target_lines}" - - ./.github/scripts/run-bazel-ci.sh \ - --print-failed-action-summary \ - "${bazel_wrapper_args[@]}" \ - -- \ - build \ - "${bazel_clippy_args[@]}" \ - -- \ - "${bazel_targets[@]}" - - - name: Upload Bazel execution logs - if: always() && !cancelled() - continue-on-error: true - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: bazel-execution-logs-clippy-${{ matrix.target }} - path: ${{ runner.temp }}/bazel-execution-logs - if-no-files-found: ignore - - # Save the job-scoped Bazel repository cache after cache misses. Keep the - # upload non-fatal so cache service issues never fail the job itself. - - name: Save bazel repository cache - if: always() && !cancelled() && steps.prepare_bazel.outputs.repository-cache-hit != 'true' - continue-on-error: true - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} - key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} - - verify-release-build: - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-24.04 - target: x86_64-unknown-linux-gnu - - os: macos-15-xlarge - target: aarch64-apple-darwin - - os: windows-latest - target: x86_64-pc-windows-gnullvm - runs-on: ${{ matrix.os }} - name: Verify release build on ${{ matrix.os }} for ${{ matrix.target }} - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Prepare Bazel CI - id: prepare_bazel - uses: ./.github/actions/prepare-bazel-ci - with: - target: ${{ matrix.target }} - cache-scope: bazel-${{ github.job }} - - - name: bazel build verify-release-build targets - env: - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - shell: bash - run: | - # This job exists to compile Rust code behind - # `cfg(not(debug_assertions))` so PR CI catches failures that would - # otherwise show up only in a release build. We do not need the full - # optimizer and debug-info work that normally comes with a release - # build to get that signal, so keep Bazel in `fastbuild` and disable - # Rust debug assertions explicitly. - bazel_wrapper_args=() - if [[ "${RUNNER_OS}" == "Windows" ]]; then - # This is build-only signal, so use the same Linux-RBE - # cross-compile path as the fast Windows test and clippy jobs. - # Fork/community PRs without the BuildBuddy secret fall back - # inside `run-bazel-ci.sh` to the previous local Windows MSVC - # host-platform shape. - bazel_wrapper_args+=(--windows-cross-compile) - fi - - bazel_build_args=( - --compilation_mode=fastbuild - --@rules_rust//rust/settings:extra_rustc_flag=-Cdebug-assertions=no - --@rules_rust//rust/settings:extra_exec_rustc_flag=-Cdebug-assertions=no - --build_metadata=COMMIT_SHA=${GITHUB_SHA} - --build_metadata=TAG_job=verify-release-build - --build_metadata=TAG_rust_debug_assertions=off - ) - - bazel_target_lines="$(bash ./scripts/list-bazel-release-targets.sh)" - bazel_targets=() - while IFS= read -r target; do - bazel_targets+=("${target}") - done <<< "${bazel_target_lines}" - - ./.github/scripts/run-bazel-ci.sh \ - "${bazel_wrapper_args[@]}" \ - -- \ - build \ - "${bazel_build_args[@]}" \ - -- \ - "${bazel_targets[@]}" - - - name: Verify Bazel builds bwrap - if: runner.os == 'Linux' - env: - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - shell: bash - run: | - ./.github/scripts/run-bazel-ci.sh \ - --remote-download-toplevel \ - --print-failed-action-summary \ - -- \ - build \ - --build_metadata=COMMIT_SHA=${GITHUB_SHA} \ - --build_metadata=TAG_job=verify-bwrap \ - -- \ - //codex-rs/bwrap:bwrap - - - name: Upload Bazel execution logs - if: always() && !cancelled() - continue-on-error: true - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: bazel-execution-logs-verify-release-build-${{ matrix.target }} - path: ${{ runner.temp }}/bazel-execution-logs - if-no-files-found: ignore - - # Save the job-scoped Bazel repository cache after cache misses. Keep the - # upload non-fatal so cache service issues never fail the job itself. - - name: Save bazel repository cache - if: always() && !cancelled() && steps.prepare_bazel.outputs.repository-cache-hit != 'true' - continue-on-error: true - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ steps.prepare_bazel.outputs.repository-cache-path }} - key: ${{ steps.prepare_bazel.outputs.repository-cache-key }} diff --git a/reference/openai-codex/.github/workflows/blob-size-policy.yml b/reference/openai-codex/.github/workflows/blob-size-policy.yml deleted file mode 100644 index 779198e..0000000 --- a/reference/openai-codex/.github/workflows/blob-size-policy.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: blob-size-policy - -on: - pull_request: {} - -jobs: - check: - name: Blob size policy - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - fetch-depth: 0 - persist-credentials: false - - - name: Determine PR comparison range - id: range - shell: bash - run: | - set -euo pipefail - echo "base=${{ github.event.pull_request.base.sha }}" >> "$GITHUB_OUTPUT" - echo "head=${{ github.event.pull_request.head.sha }}" >> "$GITHUB_OUTPUT" - - - name: Check changed blob sizes - env: - BASE_SHA: ${{ steps.range.outputs.base }} - HEAD_SHA: ${{ steps.range.outputs.head }} - run: | - python3 scripts/check_blob_size.py \ - --base "$BASE_SHA" \ - --head "$HEAD_SHA" \ - --max-bytes 512000 \ - --allowlist .github/blob-size-allowlist.txt diff --git a/reference/openai-codex/.github/workflows/cargo-deny.yml b/reference/openai-codex/.github/workflows/cargo-deny.yml deleted file mode 100644 index f20d09e..0000000 --- a/reference/openai-codex/.github/workflows/cargo-deny.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: cargo-deny - -on: - pull_request: - push: - branches: - - main - -jobs: - cargo-deny: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./codex-rs - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - - - name: Run cargo-deny - uses: EmbarkStudios/cargo-deny-action@82eb9f621fbc699dd0918f3ea06864c14cc84246 # v2 - with: - rust-version: 1.93.0 - manifest-path: ./codex-rs/Cargo.toml diff --git a/reference/openai-codex/.github/workflows/ci.yml b/reference/openai-codex/.github/workflows/ci.yml deleted file mode 100644 index b1ee139..0000000 --- a/reference/openai-codex/.github/workflows/ci.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: ci - -on: - pull_request: {} - push: { branches: [main] } - -jobs: - build-test: - runs-on: ubuntu-latest - timeout-minutes: 10 - env: - NODE_OPTIONS: --max-old-space-size=4096 - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Verify codex-rs Cargo manifests inherit workspace settings - run: python3 .github/scripts/verify_cargo_workspace_manifests.py - - - name: Verify codex-tui does not import codex-core directly - run: python3 .github/scripts/verify_tui_core_boundary.py - - - name: Verify Bazel clippy flags match Cargo workspace lints - run: python3 .github/scripts/verify_bazel_clippy_lints.py - - - name: Test Codex package builder - run: python3 -m unittest discover -s scripts/codex_package -p 'test_*.py' - - - name: Setup pnpm - uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5 - with: - run_install: false - - - name: Setup Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - with: - node-version: 22 - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Stage npm package - id: stage_npm_package - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - # Use a recent successful rust-release run that published the full - # cross-platform native payload required by the npm package layout. - # Passing the workflow URL directly avoids relying on old rust-v* - # branches remaining discoverable via `gh run list --branch ...`. - CODEX_VERSION=0.133.0-alpha.4 - WORKFLOW_URL="https://github.com/openai/codex/actions/runs/26201494185" - OUTPUT_DIR="${RUNNER_TEMP}" - python3 ./scripts/stage_npm_packages.py \ - --release-version "$CODEX_VERSION" \ - --workflow-url "$WORKFLOW_URL" \ - --package codex \ - --output-dir "$OUTPUT_DIR" - PACK_OUTPUT="${OUTPUT_DIR}/codex-npm-${CODEX_VERSION}.tgz" - echo "pack_output=$PACK_OUTPUT" >> "$GITHUB_OUTPUT" - - - name: Upload staged npm package artifact - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: codex-npm-staging - path: ${{ steps.stage_npm_package.outputs.pack_output }} - - - name: Ensure root README.md contains only ASCII and certain Unicode code points - run: ./scripts/asciicheck.py README.md - - name: Check root README ToC - run: python3 scripts/readme_toc.py README.md - - - name: Prettier (run `pnpm run format:fix` to fix) - run: pnpm run format diff --git a/reference/openai-codex/.github/workflows/cla.yml b/reference/openai-codex/.github/workflows/cla.yml deleted file mode 100644 index b48fd36..0000000 --- a/reference/openai-codex/.github/workflows/cla.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: CLA Assistant -on: - issue_comment: - types: [created] - pull_request_target: - types: [opened, closed, synchronize] - -permissions: - actions: write - contents: write - pull-requests: write - statuses: write - -jobs: - cla: - # Only run the CLA assistant for the canonical openai repo so forks are not blocked - # and contributors who signed previously do not receive duplicate CLA notifications. - if: ${{ github.repository_owner == 'openai' }} - runs-on: ubuntu-latest - steps: - - uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1 - # Run on close only if the PR was merged. This will lock the PR to preserve - # the CLA agreement. We don't want to lock PRs that have been closed without - # merging because the contributor may want to respond with additional comments. - # This action has a "lock-pullrequest-aftermerge" option that can be set to false, - # but that would unconditionally skip locking even in cases where the PR was merged. - if: | - ( - github.event_name == 'pull_request_target' && - ( - github.event.action == 'opened' || - github.event.action == 'synchronize' || - (github.event.action == 'closed' && github.event.pull_request.merged == true) - ) - ) || - ( - github.event_name == 'issue_comment' && - ( - github.event.comment.body == 'recheck' || - github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA' - ) - ) - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - path-to-document: https://github.com/openai/codex/blob/main/docs/CLA.md - path-to-signatures: signatures/cla.json - branch: cla-signatures - allowlist: codex,dependabot,dependabot[bot],github-actions[bot] diff --git a/reference/openai-codex/.github/workflows/close-stale-contributor-prs.yml b/reference/openai-codex/.github/workflows/close-stale-contributor-prs.yml deleted file mode 100644 index e8cea82..0000000 --- a/reference/openai-codex/.github/workflows/close-stale-contributor-prs.yml +++ /dev/null @@ -1,107 +0,0 @@ -name: Close stale contributor PRs - -on: - workflow_dispatch: - schedule: - - cron: "0 6 * * *" - -permissions: - contents: read - issues: write - pull-requests: write - -jobs: - close-stale-contributor-prs: - # Prevent scheduled runs on forks - if: github.repository == 'openai/codex' - runs-on: ubuntu-latest - steps: - - name: Close inactive PRs from contributors - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const DAYS_INACTIVE = 14; - const cutoff = new Date(Date.now() - DAYS_INACTIVE * 24 * 60 * 60 * 1000); - const { owner, repo } = context.repo; - const dryRun = false; - const stalePrs = []; - - core.info(`Dry run mode: ${dryRun}`); - - const prs = await github.paginate(github.rest.pulls.list, { - owner, - repo, - state: "open", - per_page: 100, - sort: "updated", - direction: "asc", - }); - - for (const pr of prs) { - const lastUpdated = new Date(pr.updated_at); - if (lastUpdated > cutoff) { - core.info(`PR ${pr.number} is fresh`); - continue; - } - - if (!pr.user || pr.user.type !== "User") { - core.info(`PR ${pr.number} wasn't created by a user`); - continue; - } - - let permission; - try { - const permissionResponse = await github.rest.repos.getCollaboratorPermissionLevel({ - owner, - repo, - username: pr.user.login, - }); - permission = permissionResponse.data.permission; - } catch (error) { - if (error.status === 404) { - core.info(`Author ${pr.user.login} is not a collaborator; skipping #${pr.number}`); - continue; - } - throw error; - } - - const hasContributorAccess = ["admin", "maintain", "write"].includes(permission); - if (!hasContributorAccess) { - core.info(`Author ${pr.user.login} has ${permission} access; skipping #${pr.number}`); - continue; - } - - stalePrs.push(pr); - } - - if (!stalePrs.length) { - core.info("No stale contributor pull requests found."); - return; - } - - for (const pr of stalePrs) { - const issue_number = pr.number; - const closeComment = `Closing this pull request because it has had no updates for more than ${DAYS_INACTIVE} days. If you plan to continue working on it, feel free to reopen or open a new PR.`; - - if (dryRun) { - core.info(`[dry-run] Would close contributor PR #${issue_number} from ${pr.user.login}`); - continue; - } - - await github.rest.issues.createComment({ - owner, - repo, - issue_number, - body: closeComment, - }); - - await github.rest.pulls.update({ - owner, - repo, - pull_number: issue_number, - state: "closed", - }); - - core.info(`Closed contributor PR #${issue_number} from ${pr.user.login}`); - } diff --git a/reference/openai-codex/.github/workflows/codespell.yml b/reference/openai-codex/.github/workflows/codespell.yml deleted file mode 100644 index aaa15cf..0000000 --- a/reference/openai-codex/.github/workflows/codespell.yml +++ /dev/null @@ -1,30 +0,0 @@ -# Codespell configuration is within .codespellrc ---- -name: Codespell - -on: - push: - branches: [main] - pull_request: - branches: [main] - -permissions: - contents: read - -jobs: - codespell: - name: Check for spelling errors - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - name: Annotate locations with typos - uses: codespell-project/codespell-problem-matcher@b80729f885d32f78a716c2f107b4db1025001c42 # v1.1.0 - - name: Codespell - uses: codespell-project/actions-codespell@8f01853be192eb0f849a5c7d721450e7a467c579 # v2.2 - with: - ignore_words_file: .codespellignore diff --git a/reference/openai-codex/.github/workflows/issue-deduplicator.yml b/reference/openai-codex/.github/workflows/issue-deduplicator.yml deleted file mode 100644 index f15c190..0000000 --- a/reference/openai-codex/.github/workflows/issue-deduplicator.yml +++ /dev/null @@ -1,420 +0,0 @@ -name: Issue Deduplicator - -on: - issues: - types: - - opened - - labeled - -jobs: - gather-duplicates-all: - name: Identify potential duplicates (all issues) - # Prevent runs on forks (requires OpenAI API key, wastes Actions minutes) - if: github.repository == 'openai/codex' && (github.event.action == 'opened' || (github.event.action == 'labeled' && github.event.label.name == 'codex-deduplicate')) - runs-on: ubuntu-latest - permissions: - contents: read - outputs: - codex_output: ${{ steps.codex-all.outputs.final-message }} - steps: - - name: Prepare Codex inputs - env: - GH_TOKEN: ${{ github.token }} - REPO: ${{ github.repository }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - run: | - set -eo pipefail - - CURRENT_ISSUE_FILE=codex-current-issue.json - EXISTING_ALL_FILE=codex-existing-issues-all.json - - gh issue list --repo "$REPO" \ - --json number,title,body,createdAt,updatedAt,state,labels \ - --limit 1000 \ - --state all \ - --search "sort:created-desc" \ - | jq '[.[] | { - number, - title, - body: ((.body // "")[0:4000]), - createdAt, - updatedAt, - state, - labels: ((.labels // []) | map(.name)) - }]' \ - > "$EXISTING_ALL_FILE" - - gh issue view "$ISSUE_NUMBER" \ - --repo "$REPO" \ - --json number,title,body \ - | jq '{number, title, body: ((.body // "")[0:4000])}' \ - > "$CURRENT_ISSUE_FILE" - - echo "Prepared duplicate detection input files." - echo "all_issue_count=$(jq 'length' "$EXISTING_ALL_FILE")" - - # Prompt instructions are intentionally inline in this workflow. The old - # .github/prompts/issue-deduplicator.txt file is obsolete and removed. - - id: codex-all - name: Find duplicates (pass 1, all issues) - uses: openai/codex-action@5c3f4ccdb2b8790f73d6b21751ac00e602aa0c02 # v1.7 - with: - openai-api-key: ${{ secrets.CODEX_OPENAI_API_KEY }} - allow-users: "*" - safety-strategy: drop-sudo - sandbox: read-only - prompt: | - You are an assistant that triages new GitHub issues by identifying potential duplicates. - - You will receive the following JSON files located in the current working directory: - - `codex-current-issue.json`: JSON object describing the newly created issue (fields: number, title, body). - - `codex-existing-issues-all.json`: JSON array of recent issues with states, timestamps, and labels. - - Instructions: - - Compare the current issue against the existing issues to find up to five that appear to describe the same underlying problem or request. - - Prioritize concrete overlap in symptoms, reproduction details, error signatures, and user intent. - - Prefer active unresolved issues when confidence is similar. - - Closed issues can still be valid duplicates if they clearly match. - - Return fewer matches rather than speculative ones. - - If confidence is low, return an empty list. - - Include at most five issue numbers. - - After analysis, provide a short reason for your decision. - - output-schema: | - { - "type": "object", - "properties": { - "issues": { - "type": "array", - "items": { - "type": "string" - } - }, - "reason": { "type": "string" } - }, - "required": ["issues", "reason"], - "additionalProperties": false - } - - normalize-duplicates-all: - name: Normalize pass 1 output - needs: gather-duplicates-all - if: ${{ needs.gather-duplicates-all.result == 'success' }} - runs-on: ubuntu-latest - permissions: {} - outputs: - issues_json: ${{ steps.normalize-all.outputs.issues_json }} - reason: ${{ steps.normalize-all.outputs.reason }} - has_matches: ${{ steps.normalize-all.outputs.has_matches }} - steps: - - id: normalize-all - name: Normalize pass 1 output - env: - CODEX_OUTPUT: ${{ needs.gather-duplicates-all.outputs.codex_output }} - CURRENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - run: | - set -eo pipefail - - raw=${CODEX_OUTPUT//$'\r'/} - parsed=false - issues='[]' - reason='' - - if [ -n "$raw" ] && printf '%s' "$raw" | jq -e 'type == "object" and (.issues | type == "array")' >/dev/null 2>&1; then - parsed=true - issues=$(printf '%s' "$raw" | jq -c '[.issues[] | tostring]') - reason=$(printf '%s' "$raw" | jq -r '.reason // ""') - else - reason='Pass 1 output was empty or invalid JSON.' - fi - - filtered=$(jq -cn --argjson issues "$issues" --arg current "$CURRENT_ISSUE_NUMBER" '[ - $issues[] - | tostring - | select(. != $current) - ] | reduce .[] as $issue ([]; if index($issue) then . else . + [$issue] end) | .[:5]') - - has_matches=false - if [ "$(jq 'length' <<< "$filtered")" -gt 0 ]; then - has_matches=true - fi - - echo "Pass 1 parsed: $parsed" - echo "Pass 1 matches after filtering: $(jq 'length' <<< "$filtered")" - echo "Pass 1 reason: $reason" - - { - echo "issues_json=$filtered" - echo "reason<> "$GITHUB_OUTPUT" - - gather-duplicates-open: - name: Identify potential duplicates (open issues fallback) - # Pass 1 Codex execution drops sudo on its runner, so run the fallback in a fresh job. - needs: normalize-duplicates-all - if: ${{ needs.normalize-duplicates-all.result == 'success' && needs.normalize-duplicates-all.outputs.has_matches != 'true' }} - runs-on: ubuntu-latest - permissions: - contents: read - outputs: - codex_output: ${{ steps.codex-open.outputs.final-message }} - steps: - - name: Prepare Codex inputs - env: - GH_TOKEN: ${{ github.token }} - REPO: ${{ github.repository }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - run: | - set -eo pipefail - - CURRENT_ISSUE_FILE=codex-current-issue.json - EXISTING_OPEN_FILE=codex-existing-issues-open.json - - gh issue list --repo "$REPO" \ - --json number,title,body,createdAt,updatedAt,state,labels \ - --limit 1000 \ - --state open \ - --search "sort:created-desc" \ - | jq '[.[] | { - number, - title, - body: ((.body // "")[0:4000]), - createdAt, - updatedAt, - state, - labels: ((.labels // []) | map(.name)) - }]' \ - > "$EXISTING_OPEN_FILE" - - gh issue view "$ISSUE_NUMBER" \ - --repo "$REPO" \ - --json number,title,body \ - | jq '{number, title, body: ((.body // "")[0:4000])}' \ - > "$CURRENT_ISSUE_FILE" - - echo "Prepared fallback duplicate detection input files." - echo "open_issue_count=$(jq 'length' "$EXISTING_OPEN_FILE")" - - - id: codex-open - name: Find duplicates (pass 2, open issues) - uses: openai/codex-action@5c3f4ccdb2b8790f73d6b21751ac00e602aa0c02 # v1.7 - with: - openai-api-key: ${{ secrets.CODEX_OPENAI_API_KEY }} - allow-users: "*" - safety-strategy: drop-sudo - sandbox: read-only - prompt: | - You are an assistant that triages new GitHub issues by identifying potential duplicates. - - This is a fallback pass because a broad search did not find convincing matches. - - You will receive the following JSON files located in the current working directory: - - `codex-current-issue.json`: JSON object describing the newly created issue (fields: number, title, body). - - `codex-existing-issues-open.json`: JSON array of open issues only. - - Instructions: - - Search only these active unresolved issues for duplicates of the current issue. - - Prioritize concrete overlap in symptoms, reproduction details, error signatures, and user intent. - - Prefer fewer, higher-confidence matches. - - If confidence is low, return an empty list. - - Include at most five issue numbers. - - After analysis, provide a short reason for your decision. - - output-schema: | - { - "type": "object", - "properties": { - "issues": { - "type": "array", - "items": { - "type": "string" - } - }, - "reason": { "type": "string" } - }, - "required": ["issues", "reason"], - "additionalProperties": false - } - - normalize-duplicates-open: - name: Normalize pass 2 output - needs: gather-duplicates-open - if: ${{ needs.gather-duplicates-open.result == 'success' }} - runs-on: ubuntu-latest - permissions: {} - outputs: - issues_json: ${{ steps.normalize-open.outputs.issues_json }} - reason: ${{ steps.normalize-open.outputs.reason }} - has_matches: ${{ steps.normalize-open.outputs.has_matches }} - steps: - - id: normalize-open - name: Normalize pass 2 output - env: - CODEX_OUTPUT: ${{ needs.gather-duplicates-open.outputs.codex_output }} - CURRENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - run: | - set -eo pipefail - - raw=${CODEX_OUTPUT//$'\r'/} - parsed=false - issues='[]' - reason='' - - if [ -n "$raw" ] && printf '%s' "$raw" | jq -e 'type == "object" and (.issues | type == "array")' >/dev/null 2>&1; then - parsed=true - issues=$(printf '%s' "$raw" | jq -c '[.issues[] | tostring]') - reason=$(printf '%s' "$raw" | jq -r '.reason // ""') - else - reason='Pass 2 output was empty or invalid JSON.' - fi - - filtered=$(jq -cn --argjson issues "$issues" --arg current "$CURRENT_ISSUE_NUMBER" '[ - $issues[] - | tostring - | select(. != $current) - ] | reduce .[] as $issue ([]; if index($issue) then . else . + [$issue] end) | .[:5]') - - has_matches=false - if [ "$(jq 'length' <<< "$filtered")" -gt 0 ]; then - has_matches=true - fi - - echo "Pass 2 parsed: $parsed" - echo "Pass 2 matches after filtering: $(jq 'length' <<< "$filtered")" - echo "Pass 2 reason: $reason" - - { - echo "issues_json=$filtered" - echo "reason<> "$GITHUB_OUTPUT" - - select-final: - name: Select final duplicate set - needs: - - normalize-duplicates-all - - normalize-duplicates-open - if: ${{ always() && needs.normalize-duplicates-all.result == 'success' && (needs.normalize-duplicates-open.result == 'success' || needs.normalize-duplicates-open.result == 'skipped') }} - runs-on: ubuntu-latest - permissions: - contents: read - outputs: - codex_output: ${{ steps.select-final.outputs.codex_output }} - steps: - - id: select-final - name: Select final duplicate set - env: - PASS1_ISSUES: ${{ needs.normalize-duplicates-all.outputs.issues_json }} - PASS1_REASON: ${{ needs.normalize-duplicates-all.outputs.reason }} - PASS2_ISSUES: ${{ needs.normalize-duplicates-open.outputs.issues_json }} - PASS2_REASON: ${{ needs.normalize-duplicates-open.outputs.reason }} - PASS1_HAS_MATCHES: ${{ needs.normalize-duplicates-all.outputs.has_matches }} - PASS2_HAS_MATCHES: ${{ needs.normalize-duplicates-open.outputs.has_matches }} - run: | - set -eo pipefail - - selected_issues='[]' - selected_reason='No plausible duplicates found.' - selected_pass='none' - - if [ "$PASS1_HAS_MATCHES" = "true" ]; then - selected_issues=${PASS1_ISSUES:-'[]'} - selected_reason=${PASS1_REASON:-'Pass 1 found duplicates.'} - selected_pass='all' - fi - - if [ "$PASS2_HAS_MATCHES" = "true" ]; then - selected_issues=${PASS2_ISSUES:-'[]'} - selected_reason=${PASS2_REASON:-'Pass 2 found duplicates.'} - selected_pass='open-fallback' - fi - - final_json=$(jq -cn \ - --argjson issues "$selected_issues" \ - --arg reason "$selected_reason" \ - --arg pass "$selected_pass" \ - '{issues: $issues, reason: $reason, pass: $pass}') - - echo "Final pass used: $selected_pass" - echo "Final duplicate count: $(jq '.issues | length' <<< "$final_json")" - echo "Final reason: $(jq -r '.reason' <<< "$final_json")" - - { - echo "codex_output<> "$GITHUB_OUTPUT" - - comment-on-issue: - name: Comment with potential duplicates - needs: select-final - if: ${{ always() && needs.select-final.result == 'success' }} - runs-on: ubuntu-latest - permissions: - contents: read - issues: write - steps: - - name: Comment on issue - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - CODEX_OUTPUT: ${{ needs.select-final.outputs.codex_output }} - with: - github-token: ${{ github.token }} - script: | - const raw = process.env.CODEX_OUTPUT ?? ''; - let parsed; - try { - parsed = JSON.parse(raw); - } catch (error) { - core.info(`Codex output was not valid JSON. Raw output: ${raw}`); - core.info(`Parse error: ${error.message}`); - return; - } - - const issues = Array.isArray(parsed?.issues) ? parsed.issues : []; - const currentIssueNumber = String(context.payload.issue.number); - const passUsed = typeof parsed?.pass === 'string' ? parsed.pass : 'unknown'; - const reason = typeof parsed?.reason === 'string' ? parsed.reason : ''; - - console.log(`Current issue number: ${currentIssueNumber}`); - console.log(`Pass used: ${passUsed}`); - if (reason) { - console.log(`Reason: ${reason}`); - } - console.log(issues); - - const filteredIssues = [...new Set(issues.map((value) => String(value)))].filter((value) => value !== currentIssueNumber).slice(0, 5); - - if (filteredIssues.length === 0) { - core.info('Codex reported no potential duplicates.'); - return; - } - - const lines = [ - 'Potential duplicates detected. Please review them and close your issue if it is a duplicate.', - '', - ...filteredIssues.map((value) => `- #${String(value)}`), - '', - '*Powered by [Codex Action](https://github.com/openai/codex-action)*']; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.issue.number, - body: lines.join("\n"), - }); - - - name: Remove codex-deduplicate label - if: ${{ always() && github.event.action == 'labeled' && github.event.label.name == 'codex-deduplicate' }} - env: - GH_TOKEN: ${{ github.token }} - GH_REPO: ${{ github.repository }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - run: | - gh issue edit "$ISSUE_NUMBER" --remove-label codex-deduplicate || true - echo "Attempted to remove label: codex-deduplicate" diff --git a/reference/openai-codex/.github/workflows/issue-labeler.yml b/reference/openai-codex/.github/workflows/issue-labeler.yml deleted file mode 100644 index 77fe5d0..0000000 --- a/reference/openai-codex/.github/workflows/issue-labeler.yml +++ /dev/null @@ -1,151 +0,0 @@ -name: Issue Labeler - -on: - issues: - types: - - opened - - labeled - -jobs: - gather-labels: - name: Generate label suggestions - # Prevent runs on forks (requires OpenAI API key, wastes Actions minutes) - if: github.repository == 'openai/codex' && (github.event.action == 'opened' || (github.event.action == 'labeled' && github.event.label.name == 'codex-label')) - runs-on: ubuntu-latest - permissions: - contents: read - outputs: - codex_output: ${{ steps.codex.outputs.final-message }} - steps: - - id: codex - uses: openai/codex-action@5c3f4ccdb2b8790f73d6b21751ac00e602aa0c02 # v1.7 - with: - openai-api-key: ${{ secrets.CODEX_OPENAI_API_KEY }} - allow-users: "*" - safety-strategy: drop-sudo - sandbox: read-only - prompt: | - You are an assistant that reviews GitHub issues for the repository. - - Your job is to choose the most appropriate labels for the issue described later in this prompt. - Follow these rules: - - - Add one (and only one) of the following three labels to distinguish the type of issue. Default to "bug" if unsure. - 1. bug — Reproducible defects in Codex products (CLI, VS Code extension, web, auth). - 2. enhancement — Feature requests or usability improvements that ask for new capabilities, better ergonomics, or quality-of-life tweaks. - 3. documentation — Updates or corrections needed in docs/README/config references (broken links, missing examples, outdated keys, clarification requests). - - - If applicable, add one of the following labels to specify which sub-product or product surface the issue relates to. - 1. CLI — the Codex command line interface. - 2. extension — VS Code (or other IDE) extension-specific issues. - 3. app - Issues related to the Codex desktop application. - 4. codex-web — Issues targeting the Codex web UI/Cloud experience. - 5. github-action — Issues with the Codex GitHub action. - 6. iOS — Issues with the Codex iOS app. - - - Additionally add zero or more of the following labels that are relevant to the issue content. Prefer a small set of precise labels over many broad ones. - - For agent-area issues, prefer the most specific applicable label. Use "agent" only as a fallback for agent-related issues that do not fit a more specific agent-area label. Prefer "app-server" over "session" or "config" when the issue is about app-server protocol, API, RPC, schema, launch, or bridge behavior. Use "memory" for agentic memory storage/retrieval and "performance" for high process memory utilization or memory leaks. - 1. windows-os — Bugs or friction specific to Windows environments (always when PowerShell is mentioned, path handling, copy/paste, OS-specific auth or tooling failures). - 2. mcp — Topics involving Model Context Protocol servers/clients. - 3. mcp-server — Problems related to the codex mcp-server command, where codex runs as an MCP server. - 4. azure — Problems or requests tied to Azure OpenAI deployments. - 5. model-behavior — Undesirable LLM behavior: forgetting goals, refusing work, hallucinating environment details, quota misreports, or other reasoning/performance anomalies. - 6. code-review — Issues related to the code review feature or functionality. - 7. safety-check - Issues related to cyber risk detection or trusted access verification. - 8. auth - Problems related to authentication, login, or access tokens. - 9. exec - Problems related to the "codex exec" command or functionality. - 10. hooks - Problems related to event hooks - 11. context - Problems related to compaction, context windows, or available context reporting. - 12. skills - Problems related to skills or plugins - 13. custom-model - Problems that involve using custom model providers, local models, or OSS models. - 14. rate-limits - Problems related to token limits, rate limits, or token usage reporting. - 15. sandbox - Issues related to local sandbox environments or tool call approvals to override sandbox restrictions. - 16. tool-calls - Problems related to specific tool call invocations including unexpected errors, failures, or hangs. - 17. TUI - Problems with the terminal user interface (TUI) including keyboard shortcuts, copy & pasting, menus, or screen update issues. - 18. app-server - Issues involving the app-server protocol or interfaces, including SDK/API payloads, thread/* and turn/* RPCs, app-server launch behavior, external app/controller bridges, and app-server protocol/schema behavior. - 19. connectivity - Network connectivity or endpoint issues, including reconnecting messages, stream dropped/disconnected errors, websocket/SSE/transport failures, timeout/network/VPN/proxy/API endpoint failures, and related retry behavior. - 20. subagent - Issues involving subagents, sub-agents, or multi-agent behavior, including spawn_agent, wait_agent, close_agent, worker/explorer roles, delegation, agent teams, lifecycle, model/config inheritance, quotas, and orchestration. - 21. session - Issues involving session or thread management, including resume, fork, archive, rename/title, thread history, rollout persistence, compaction, checkpoints, retention, and cross-session state. - 22. config - Issues involving config.toml, config keys, config key merging, config updates, profiles, hooks config, project config, agent role TOMLs, instruction/personality config, and config schema behavior. - 23. plan - Issues involving plan mode, planning workflows, or plan-specific tools/behavior. - 24. computer-use - Issues involving agentic computer use or SkyComputerUseService. - 25. browser - Issues involving agentic browser use, IAB, or the built-in browser within the Codex app. - 26. memory - Issues involving agentic memory storage and retrieval. - 27. imagen - Issues involving image generation. - 28. remote - Issues involving remote access, remote control, or SSH. - 29. performance - Issues involving slow, laggy performance, high memory utilization, or memory leaks. - 30. automations - Issues involving scheduled automation tasks or heartbeats. - 31. pets - Issues involving pets avatars and animations. - 32. agent - Fallback only for core agent loop or agent-related issues that do not fit app-server, connectivity, subagent, session, config, plan, computer-use, browser, memory, imagen, remote, performance, automations, or pets. - - Issue number: ${{ github.event.issue.number }} - - Issue title: - ${{ github.event.issue.title }} - - Issue body: - ${{ github.event.issue.body }} - - Repository full name: - ${{ github.repository }} - - output-schema: | - { - "type": "object", - "properties": { - "labels": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": ["labels"], - "additionalProperties": false - } - - apply-labels: - name: Apply labels from Codex output - needs: gather-labels - if: ${{ needs.gather-labels.result != 'skipped' }} - runs-on: ubuntu-latest - permissions: - contents: read - issues: write - env: - GH_TOKEN: ${{ github.token }} - GH_REPO: ${{ github.repository }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - CODEX_OUTPUT: ${{ needs.gather-labels.outputs.codex_output }} - steps: - - name: Apply labels - run: | - json=${CODEX_OUTPUT//$'\r'/} - if [ -z "$json" ]; then - echo "Codex produced no output. Skipping label application." - exit 0 - fi - - if ! printf '%s' "$json" | jq -e 'type == "object" and (.labels | type == "array")' >/dev/null 2>&1; then - echo "Codex output did not include a labels array. Raw output: $json" - exit 0 - fi - - labels=$(printf '%s' "$json" | jq -r '.labels[] | tostring') - if [ -z "$labels" ]; then - echo "Codex returned an empty array. Nothing to do." - exit 0 - fi - - cmd=(gh issue edit "$ISSUE_NUMBER") - while IFS= read -r label; do - cmd+=(--add-label "$label") - done <<< "$labels" - - "${cmd[@]}" || true - - - name: Remove codex-label trigger - if: ${{ always() && github.event.action == 'labeled' && github.event.label.name == 'codex-label' }} - run: | - gh issue edit "$ISSUE_NUMBER" --remove-label codex-label || true - echo "Attempted to remove label: codex-label" diff --git a/reference/openai-codex/.github/workflows/rust-ci-full-nextest-platform.yml b/reference/openai-codex/.github/workflows/rust-ci-full-nextest-platform.yml deleted file mode 100644 index 7dc39d3..0000000 --- a/reference/openai-codex/.github/workflows/rust-ci-full-nextest-platform.yml +++ /dev/null @@ -1,464 +0,0 @@ -name: rust-ci-full nextest platform - -on: - workflow_call: - inputs: - runner: - required: true - type: string - runner_group: - required: false - default: "" - type: string - runner_labels: - required: false - default: "" - type: string - archive_runner: - required: false - default: "" - type: string - archive_runner_group: - required: false - default: "" - type: string - archive_runner_labels: - required: false - default: "" - type: string - target: - required: true - type: string - profile: - required: true - type: string - artifact_id: - required: true - type: string - remote_env: - required: false - default: false - type: boolean - test_threads: - required: false - default: 0 - type: number - use_sccache: - required: false - default: false - type: boolean - -# Caller workflow-level env does not flow through workflow_call, so keep the -# Cargo git transport hardening on the archive and shard jobs directly here. -env: - CARGO_NET_GIT_FETCH_WITH_CLI: "true" - -jobs: - archive: - name: Build nextest archive - runs-on: ${{ inputs.archive_runner_group != '' && fromJSON(format('{{"group":"{0}","labels":"{1}"}}', inputs.archive_runner_group, inputs.archive_runner_labels)) || inputs.archive_runner != '' && inputs.archive_runner || inputs.runner_group != '' && fromJSON(format('{{"group":"{0}","labels":"{1}"}}', inputs.runner_group, inputs.runner_labels)) || inputs.runner }} - timeout-minutes: 60 - defaults: - run: - working-directory: codex-rs - env: - # Windows ARM64 archives are built on Windows x64, while their shards run - # on native Windows ARM64. Key producer-side caches by the archive runner - # so the cross-compile build reuses the Windows x64 cache lineage. - ARCHIVE_CACHE_RUNNER: ${{ inputs.archive_runner != '' && inputs.archive_runner || inputs.runner }} - USE_SCCACHE: ${{ inputs.use_sccache && 'true' || 'false' }} - CARGO_INCREMENTAL: "0" - SCCACHE_CACHE_SIZE: 10G - NEXTEST_ARCHIVE_FILE: nextest-${{ inputs.artifact_id }}.tar.zst - TEST_HELPERS_ARTIFACT: nextest-test-helpers-${{ inputs.artifact_id }} - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Configure Dev Drive (Windows) - if: ${{ runner.os == 'Windows' }} - shell: pwsh - run: ../.github/scripts/setup-dev-drive.ps1 - - - name: Install Linux build dependencies - if: ${{ runner.os == 'Linux' }} - shell: bash - run: | - set -euo pipefail - if command -v apt-get >/dev/null 2>&1; then - sudo apt-get update -y - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev bubblewrap - fi - - - name: Install DotSlash - uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2 - - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - targets: ${{ inputs.target }} - - - name: Expose MSVC SDK environment (Windows) - if: ${{ runner.os == 'Windows' && inputs.target == 'aarch64-pc-windows-msvc' }} - uses: ./.github/actions/setup-msvc-env - with: - target: ${{ inputs.target }} - - - name: Compute lockfile hash - id: lockhash - shell: bash - run: | - set -euo pipefail - echo "hash=$(sha256sum Cargo.lock | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" - echo "toolchain_hash=$(sha256sum rust-toolchain.toml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" - - - name: Restore cargo home cache - id: cache_cargo_home_restore - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: cargo-home-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-${{ steps.lockhash.outputs.hash }}-${{ steps.lockhash.outputs.toolchain_hash }} - restore-keys: | - cargo-home-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}- - - - name: Install sccache - if: ${{ env.USE_SCCACHE == 'true' }} - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: sccache - version: 0.7.5 - - - name: Configure sccache backend - if: ${{ env.USE_SCCACHE == 'true' }} - shell: bash - run: | - set -euo pipefail - if [[ -n "${ACTIONS_CACHE_URL:-}" && -n "${ACTIONS_RUNTIME_TOKEN:-}" ]]; then - echo "SCCACHE_GHA_ENABLED=true" >> "$GITHUB_ENV" - echo "Using sccache GitHub backend" - else - echo "SCCACHE_GHA_ENABLED=false" >> "$GITHUB_ENV" - if [[ -n "${DEV_DRIVE:-}" ]]; then - echo "SCCACHE_DIR=${DEV_DRIVE}\\.sccache" >> "$GITHUB_ENV" - else - echo "SCCACHE_DIR=${{ github.workspace }}/.sccache" >> "$GITHUB_ENV" - fi - echo "Using sccache local disk + actions/cache fallback" - fi - - - name: Enable sccache wrapper - if: ${{ env.USE_SCCACHE == 'true' }} - shell: bash - run: | - set -euo pipefail - wrapper="$(command -v sccache)" - if [[ "${RUNNER_OS}" == "Windows" ]] && command -v cygpath >/dev/null 2>&1; then - wrapper="$(cygpath -w "${wrapper}")" - fi - echo "RUSTC_WRAPPER=${wrapper}" >> "$GITHUB_ENV" - echo "CARGO_BUILD_RUSTC_WRAPPER=${wrapper}" >> "$GITHUB_ENV" - - - name: Restore sccache cache (fallback) - if: ${{ env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true' }} - id: cache_sccache_restore - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ env.SCCACHE_DIR }} - key: sccache-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }} - restore-keys: | - sccache-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-${{ steps.lockhash.outputs.hash }}- - sccache-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}- - - - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: nextest - version: 0.9.103 - - - name: Enable unprivileged user namespaces (Linux) - if: runner.os == 'Linux' - run: | - sudo sysctl -w kernel.unprivileged_userns_clone=1 - if sudo sysctl -a 2>/dev/null | grep -q '^kernel.apparmor_restrict_unprivileged_userns'; then - sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - fi - - - name: Build nextest archive - shell: bash - run: | - set -euo pipefail - archive_dir="${RUNNER_TEMP}/nextest-archive" - mkdir -p "${archive_dir}" - cargo nextest archive \ - --target ${{ inputs.target }} \ - --cargo-profile ${{ inputs.profile }} \ - --timings \ - --archive-file "${archive_dir}/${NEXTEST_ARCHIVE_FILE}" - - - name: Build runtime test helpers - if: ${{ runner.os == 'Linux' || runner.os == 'Windows' }} - shell: bash - run: | - set -euo pipefail - helper_dir="${RUNNER_TEMP}/${TEST_HELPERS_ARTIFACT}" - mkdir -p "${helper_dir}" - - if [[ "${RUNNER_OS}" == "Linux" ]]; then - cargo build \ - --target ${{ inputs.target }} \ - --profile ${{ inputs.profile }} \ - -p codex-linux-sandbox \ - --bin codex-linux-sandbox - cp "target/${{ inputs.target }}/${{ inputs.profile }}/codex-linux-sandbox" "${helper_dir}/" - else - cargo build \ - --target ${{ inputs.target }} \ - --profile ${{ inputs.profile }} \ - -p codex-windows-sandbox \ - --bin codex-windows-sandbox-setup \ - --bin codex-command-runner - cp "target/${{ inputs.target }}/${{ inputs.profile }}/codex-windows-sandbox-setup.exe" "${helper_dir}/" - cp "target/${{ inputs.target }}/${{ inputs.profile }}/codex-command-runner.exe" "${helper_dir}/" - fi - - - name: Upload Cargo timings (nextest) - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: cargo-timings-rust-ci-nextest-${{ inputs.target }}-${{ inputs.profile }} - path: codex-rs/target/**/cargo-timings/cargo-timing.html - if-no-files-found: warn - - - name: Upload nextest archive - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: nextest-archive-${{ inputs.artifact_id }} - path: ${{ runner.temp }}/nextest-archive/${{ env.NEXTEST_ARCHIVE_FILE }} - if-no-files-found: error - retention-days: 1 - - - name: Upload runtime test helpers - if: ${{ runner.os == 'Linux' || runner.os == 'Windows' }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: ${{ env.TEST_HELPERS_ARTIFACT }} - path: ${{ runner.temp }}/${{ env.TEST_HELPERS_ARTIFACT }}/* - if-no-files-found: error - retention-days: 1 - - - name: Save cargo home cache - if: always() && !cancelled() && steps.cache_cargo_home_restore.outputs.cache-hit != 'true' - continue-on-error: true - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: cargo-home-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-${{ steps.lockhash.outputs.hash }}-${{ steps.lockhash.outputs.toolchain_hash }} - - - name: Save sccache cache (fallback) - if: always() && !cancelled() && env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true' - continue-on-error: true - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ env.SCCACHE_DIR }} - key: sccache-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }} - - - name: sccache stats - if: always() && env.USE_SCCACHE == 'true' - continue-on-error: true - run: sccache --show-stats || true - - - name: sccache summary - if: always() && env.USE_SCCACHE == 'true' - shell: bash - run: | - { - echo "### sccache stats — ${{ inputs.target }} (tests)"; - echo; - echo '```'; - sccache --show-stats || true; - echo '```'; - } >> "$GITHUB_STEP_SUMMARY" - - shard: - name: Tests shard ${{ matrix.shard }}/4 - needs: archive - runs-on: ${{ inputs.runner_group != '' && fromJSON(format('{{"group":"{0}","labels":"{1}"}}', inputs.runner_group, inputs.runner_labels)) || inputs.runner }} - timeout-minutes: 60 - defaults: - run: - working-directory: codex-rs - env: - NEXTEST_ARCHIVE_FILE: nextest-${{ inputs.artifact_id }}.tar.zst - TEST_HELPERS_ARTIFACT: nextest-test-helpers-${{ inputs.artifact_id }} - strategy: - fail-fast: false - matrix: - shard: [1, 2, 3, 4] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Install Linux build dependencies - if: ${{ runner.os == 'Linux' }} - shell: bash - run: | - set -euo pipefail - if command -v apt-get >/dev/null 2>&1; then - sudo apt-get update -y - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev bubblewrap - fi - - - name: Install DotSlash - uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2 - - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - targets: ${{ inputs.target }} - - - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: nextest - version: 0.9.103 - - - name: Enable unprivileged user namespaces (Linux) - if: runner.os == 'Linux' - run: | - sudo sysctl -w kernel.unprivileged_userns_clone=1 - if sudo sysctl -a 2>/dev/null | grep -q '^kernel.apparmor_restrict_unprivileged_userns'; then - sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - fi - - - name: Set up remote test env (Docker) - if: ${{ runner.os == 'Linux' && inputs.remote_env }} - shell: bash - run: | - set -euo pipefail - export CODEX_TEST_REMOTE_ENV_CONTAINER_NAME="codex-remote-test-env-${{ github.run_id }}-${{ matrix.shard }}" - source "${GITHUB_WORKSPACE}/scripts/test-remote-env.sh" - echo "CODEX_TEST_REMOTE_ENV=${CODEX_TEST_REMOTE_ENV}" >> "$GITHUB_ENV" - echo "CODEX_TEST_REMOTE_EXEC_SERVER_URL=${CODEX_TEST_REMOTE_EXEC_SERVER_URL}" >> "$GITHUB_ENV" - - - name: Download nextest archive - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: nextest-archive-${{ inputs.artifact_id }} - path: ${{ runner.temp }}/nextest-archive - - - name: Download runtime test helpers - if: ${{ runner.os == 'Linux' || runner.os == 'Windows' }} - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: ${{ env.TEST_HELPERS_ARTIFACT }} - path: ${{ runner.temp }}/${{ env.TEST_HELPERS_ARTIFACT }} - - - name: tests - id: test - shell: bash - run: | - set -euo pipefail - archive_file="${RUNNER_TEMP}/nextest-archive/${NEXTEST_ARCHIVE_FILE}" - workspace_root="$(pwd)" - - if [[ "${RUNNER_OS}" == "Windows" ]]; then - archive_file="$(cygpath -w "${archive_file}")" - workspace_root="$(cygpath -w "${workspace_root}")" - fi - - if [[ "${RUNNER_OS}" == "Linux" ]]; then - helper_dir="${RUNNER_TEMP}/${TEST_HELPERS_ARTIFACT}" - helper_target_dir="$(pwd)/target/${{ inputs.target }}/${{ inputs.profile }}" - mkdir -p "${helper_target_dir}" - cp "${helper_dir}/codex-linux-sandbox" "${helper_target_dir}/" - chmod +x "${helper_target_dir}/codex-linux-sandbox" - elif [[ "${RUNNER_OS}" == "Windows" ]]; then - helper_dir="${RUNNER_TEMP}/${TEST_HELPERS_ARTIFACT}" - helper_target_dir="$(pwd)/target/${{ inputs.target }}/${{ inputs.profile }}" - mkdir -p "${helper_target_dir}" - cp "${helper_dir}/codex-windows-sandbox-setup.exe" "${helper_target_dir}/" - cp "${helper_dir}/codex-command-runner.exe" "${helper_target_dir}/" - fi - - nextest_args=( - run - --no-fail-fast - --archive-file "${archive_file}" - --workspace-remap "${workspace_root}" - --partition "hash:${{ matrix.shard }}/4" - ) - if [[ "${{ inputs.test_threads }}" != "0" ]]; then - nextest_args+=(--test-threads "${{ inputs.test_threads }}") - fi - - test_command=(cargo nextest "${nextest_args[@]}") - if [[ "${RUNNER_OS}" == "Linux" ]]; then - sandbox_helper="${helper_target_dir}/codex-linux-sandbox" - test_command=( - env - "CARGO_BIN_EXE_codex-linux-sandbox=${sandbox_helper}" - "CARGO_BIN_EXE_codex_linux_sandbox=${sandbox_helper}" - cargo nextest "${nextest_args[@]}" - ) - elif [[ "${RUNNER_OS}" == "Windows" ]]; then - setup_helper="$(cygpath -w "${helper_target_dir}/codex-windows-sandbox-setup.exe")" - command_runner="$(cygpath -w "${helper_target_dir}/codex-command-runner.exe")" - test_command=( - env - "CARGO_BIN_EXE_codex_windows_sandbox_setup=${setup_helper}" - "CARGO_BIN_EXE_codex_command_runner=${command_runner}" - cargo nextest "${nextest_args[@]}" - ) - fi - - "${test_command[@]}" - env: - RUST_BACKTRACE: 1 - RUST_MIN_STACK: "8388608" # 8 MiB - NEXTEST_STATUS_LEVEL: leak - - - name: Upload nextest JUnit report - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: nextest-junit-rust-ci-${{ inputs.artifact_id }}-shard-${{ matrix.shard }} - path: codex-rs/target/nextest/default/junit.xml - if-no-files-found: warn - - - name: Tear down remote test env - if: ${{ always() && runner.os == 'Linux' && inputs.remote_env }} - shell: bash - run: | - set +e - if [[ "${STEPS_TEST_OUTCOME}" != "success" ]]; then - docker logs "${CODEX_TEST_REMOTE_ENV}" || true - fi - docker rm -f "${CODEX_TEST_REMOTE_ENV}" >/dev/null 2>&1 || true - env: - STEPS_TEST_OUTCOME: ${{ steps.test.outcome }} - - - name: verify tests passed - if: steps.test.outcome == 'failure' - run: | - echo "Tests failed. See logs for details." - exit 1 - - result: - name: Platform result - needs: shard - if: always() - runs-on: ubuntu-24.04 - steps: - - name: Confirm test shards passed - shell: bash - run: | - if [[ "${{ needs.shard.result }}" != "success" ]]; then - echo "Nextest shards finished with result: ${{ needs.shard.result }}" >&2 - exit 1 - fi diff --git a/reference/openai-codex/.github/workflows/rust-ci-full.yml b/reference/openai-codex/.github/workflows/rust-ci-full.yml deleted file mode 100644 index c4a9329..0000000 --- a/reference/openai-codex/.github/workflows/rust-ci-full.yml +++ /dev/null @@ -1,643 +0,0 @@ -name: rust-ci-full -on: - push: - branches: - - main - - "**full-ci**" - workflow_dispatch: - -# CI builds in debug (dev) for faster signal. -env: - # Cargo's libgit2 transport has been flaky on macOS when fetching git - # dependencies with nested submodules. Use the system git CLI, which has - # better network/proxy behavior and matches Cargo's own suggested fallback. - CARGO_NET_GIT_FETCH_WITH_CLI: "true" - -jobs: - # --- CI that doesn't need specific targets --------------------------------- - general: - name: Format / etc - runs-on: ubuntu-24.04 - defaults: - run: - working-directory: codex-rs - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - components: rustfmt - - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: just - - name: cargo fmt - run: cargo fmt -- --config imports_granularity=Item --check - - name: Rust benchmark smoke test - run: just bench-smoke - - cargo_shear: - name: cargo shear - runs-on: ubuntu-24.04 - defaults: - run: - working-directory: codex-rs - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: cargo-shear@1.11.2 - - name: cargo shear - run: cargo shear --deny-warnings - - argument_comment_lint_package: - name: Argument comment lint package - runs-on: ubuntu-24.04 - env: - CARGO_DYLINT_VERSION: 5.0.0 - DYLINT_LINK_VERSION: 5.0.0 - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - toolchain: nightly-2025-09-18 - components: llvm-tools-preview, rustc-dev, rust-src - - name: Cache cargo-dylint tooling - id: cargo_dylint_cache - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - ~/.cargo/bin/cargo-dylint - ~/.cargo/bin/dylint-link - ~/.cargo/registry/index - ~/.cargo/registry/cache - ~/.cargo/git/db - key: argument-comment-lint-${{ runner.os }}-${{ env.CARGO_DYLINT_VERSION }}-${{ env.DYLINT_LINK_VERSION }}-${{ hashFiles('tools/argument-comment-lint/Cargo.lock', 'tools/argument-comment-lint/rust-toolchain', '.github/workflows/rust-ci.yml', '.github/workflows/rust-ci-full.yml') }} - - name: Install cargo-dylint tooling - if: ${{ steps.cargo_dylint_cache.outputs.cache-hit != 'true' }} - shell: bash - run: | - cargo install --locked cargo-dylint --version "$CARGO_DYLINT_VERSION" - cargo install --locked dylint-link --version "$DYLINT_LINK_VERSION" - - name: Check Python wrapper syntax - run: python3 -m py_compile tools/argument-comment-lint/wrapper_common.py tools/argument-comment-lint/run.py tools/argument-comment-lint/run-prebuilt-linter.py tools/argument-comment-lint/test_wrapper_common.py - - name: Test Python wrapper helpers - run: python3 -m unittest discover -s tools/argument-comment-lint -p 'test_*.py' - - name: Test argument comment lint package - working-directory: tools/argument-comment-lint - run: cargo test - env: - RUST_MIN_STACK: "8388608" # 8 MiB - - argument_comment_lint_prebuilt: - name: Argument comment lint - ${{ matrix.name }} - runs-on: ${{ matrix.runs_on || matrix.runner }} - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - include: - - name: Linux - runner: ubuntu-24.04 - - name: macOS - runner: macos-15-xlarge - - name: Windows - runner: windows-x64 - runs_on: - group: codex-runners - labels: codex-windows-x64 - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - uses: ./.github/actions/setup-bazel-ci - with: - target: ${{ runner.os }} - install-test-prereqs: true - - name: Install Linux sandbox build dependencies - if: ${{ runner.os == 'Linux' }} - shell: bash - run: | - sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev - - name: Run argument comment lint on codex-rs via Bazel - if: ${{ runner.os != 'Windows' }} - env: - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - shell: bash - run: | - bazel_targets="$(./tools/argument-comment-lint/list-bazel-targets.sh)" - ./.github/scripts/run-bazel-ci.sh \ - -- \ - build \ - --config=argument-comment-lint \ - --keep_going \ - --build_metadata=COMMIT_SHA=${GITHUB_SHA} \ - -- \ - ${bazel_targets} - - name: Run argument comment lint on codex-rs via Bazel - if: ${{ runner.os == 'Windows' }} - env: - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - shell: bash - run: | - ./.github/scripts/run-argument-comment-lint-bazel.sh \ - --config=argument-comment-lint \ - --platforms=//:local_windows \ - --keep_going \ - --build_metadata=COMMIT_SHA=${GITHUB_SHA} - - # --- CI to validate on different os/targets -------------------------------- - lint_build: - name: Lint/Build — ${{ matrix.runner }} - ${{ matrix.target }}${{ matrix.profile == 'release' && ' (release)' || '' }} - runs-on: ${{ matrix.runs_on || matrix.runner }} - timeout-minutes: 30 - defaults: - run: - working-directory: codex-rs - env: - # Speed up repeated builds across CI runs by caching compiled objects, except on - # arm64 macOS runners cross-targeting x86_64 where ring/cc-rs can produce - # mixed-architecture archives under sccache. - USE_SCCACHE: ${{ (startsWith(matrix.runner, 'windows') || (matrix.runner == 'macos-15-xlarge' && matrix.target == 'x86_64-apple-darwin')) && 'false' || 'true' }} - CARGO_INCREMENTAL: "0" - SCCACHE_CACHE_SIZE: 10G - # In rust-ci, representative release-profile checks use thin LTO for faster feedback. - CARGO_PROFILE_RELEASE_LTO: ${{ matrix.profile == 'release' && 'thin' || 'fat' }} - - strategy: - fail-fast: false - matrix: - include: - - runner: macos-15-xlarge - target: aarch64-apple-darwin - profile: dev - - runner: macos-15-xlarge - target: x86_64-apple-darwin - profile: dev - - runner: ubuntu-24.04 - target: x86_64-unknown-linux-musl - profile: dev - runs_on: - group: codex-runners - labels: codex-linux-x64 - - runner: ubuntu-24.04 - target: x86_64-unknown-linux-gnu - profile: dev - runs_on: - group: codex-runners - labels: codex-linux-x64 - - runner: ubuntu-24.04-arm - target: aarch64-unknown-linux-musl - profile: dev - runs_on: - group: codex-runners - labels: codex-linux-arm64 - - runner: ubuntu-24.04-arm - target: aarch64-unknown-linux-gnu - profile: dev - runs_on: - group: codex-runners - labels: codex-linux-arm64 - - runner: windows-x64 - target: x86_64-pc-windows-msvc - profile: dev - runs_on: - group: codex-runners - labels: codex-windows-x64 - - runner: windows-arm64 - target: aarch64-pc-windows-msvc - profile: dev - runs_on: - group: codex-runners - labels: codex-windows-arm64 - - # Also run representative release builds on Mac and Linux because - # there could be release-only build errors we want to catch. - # Hopefully this also pre-populates the build cache to speed up - # releases. - - runner: macos-15-xlarge - target: aarch64-apple-darwin - profile: release - - runner: ubuntu-24.04 - target: x86_64-unknown-linux-musl - profile: release - runs_on: - group: codex-runners - labels: codex-linux-x64 - - runner: ubuntu-24.04-arm - target: aarch64-unknown-linux-musl - profile: release - runs_on: - group: codex-runners - labels: codex-linux-arm64 - - runner: windows-x64 - target: x86_64-pc-windows-msvc - profile: release - runs_on: - group: codex-runners - labels: codex-windows-x64 - - runner: windows-arm64 - target: aarch64-pc-windows-msvc - profile: release - runs_on: - group: codex-runners - labels: codex-windows-arm64 - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: Install Linux build dependencies - if: ${{ runner.os == 'Linux' }} - shell: bash - run: | - set -euo pipefail - if command -v apt-get >/dev/null 2>&1; then - sudo apt-get update -y - packages=(pkg-config libcap-dev) - if [[ "${{ matrix.target }}" == 'x86_64-unknown-linux-musl' || "${{ matrix.target }}" == 'aarch64-unknown-linux-musl' ]]; then - packages+=(libubsan1) - fi - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${packages[@]}" - fi - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - targets: ${{ matrix.target }} - components: clippy - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Use hermetic Cargo home (musl) - shell: bash - run: | - set -euo pipefail - cargo_home="${GITHUB_WORKSPACE}/.cargo-home" - mkdir -p "${cargo_home}/bin" - echo "CARGO_HOME=${cargo_home}" >> "$GITHUB_ENV" - echo "${cargo_home}/bin" >> "$GITHUB_PATH" - : > "${cargo_home}/config.toml" - - - name: Compute lockfile hash - id: lockhash - working-directory: codex-rs - shell: bash - run: | - set -euo pipefail - echo "hash=$(sha256sum Cargo.lock | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" - echo "toolchain_hash=$(sha256sum rust-toolchain.toml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" - - # Explicit cache restore: split cargo home vs target, so we can - # avoid caching the large target dir on the gnu-dev job. - - name: Restore cargo home cache - id: cache_cargo_home_restore - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - ${{ github.workspace }}/.cargo-home/bin/ - ${{ github.workspace }}/.cargo-home/registry/index/ - ${{ github.workspace }}/.cargo-home/registry/cache/ - ${{ github.workspace }}/.cargo-home/git/db/ - key: cargo-home-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ steps.lockhash.outputs.toolchain_hash }} - restore-keys: | - cargo-home-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}- - - # Install and restore sccache cache - - name: Install sccache - if: ${{ env.USE_SCCACHE == 'true' }} - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: sccache - version: 0.7.5 - - - name: Configure sccache backend - if: ${{ env.USE_SCCACHE == 'true' }} - shell: bash - run: | - set -euo pipefail - if [[ -n "${ACTIONS_CACHE_URL:-}" && -n "${ACTIONS_RUNTIME_TOKEN:-}" ]]; then - echo "SCCACHE_GHA_ENABLED=true" >> "$GITHUB_ENV" - echo "Using sccache GitHub backend" - else - echo "SCCACHE_GHA_ENABLED=false" >> "$GITHUB_ENV" - echo "SCCACHE_DIR=${{ github.workspace }}/.sccache" >> "$GITHUB_ENV" - echo "Using sccache local disk + actions/cache fallback" - fi - - - name: Enable sccache wrapper - if: ${{ env.USE_SCCACHE == 'true' }} - shell: bash - run: echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV" - - - name: Restore sccache cache (fallback) - if: ${{ env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true' }} - id: cache_sccache_restore - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ github.workspace }}/.sccache/ - key: sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }} - restore-keys: | - sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}- - sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}- - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Disable sccache wrapper (musl) - shell: bash - run: | - set -euo pipefail - echo "RUSTC_WRAPPER=" >> "$GITHUB_ENV" - echo "RUSTC_WORKSPACE_WRAPPER=" >> "$GITHUB_ENV" - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Prepare APT cache directories (musl) - shell: bash - run: | - set -euo pipefail - sudo mkdir -p /var/cache/apt/archives /var/lib/apt/lists - sudo chown -R "$USER:$USER" /var/cache/apt /var/lib/apt/lists - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Restore APT cache (musl) - id: cache_apt_restore - uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - /var/cache/apt - key: apt-${{ matrix.runner }}-${{ matrix.target }}-v1 - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Install Zig - uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 - with: - version: 0.14.0 - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Install musl build tools - env: - DEBIAN_FRONTEND: noninteractive - TARGET: ${{ matrix.target }} - APT_UPDATE_ARGS: -o Acquire::Retries=3 - APT_INSTALL_ARGS: --no-install-recommends - shell: bash - run: bash "${GITHUB_WORKSPACE}/.github/scripts/install-musl-build-tools.sh" - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Configure rustc UBSan wrapper (musl host) - shell: bash - run: | - set -euo pipefail - ubsan="" - if command -v ldconfig >/dev/null 2>&1; then - ubsan="$(ldconfig -p | grep -m1 'libubsan\.so\.1' | sed -E 's/.*=> (.*)$/\1/')" - fi - wrapper_root="${RUNNER_TEMP:-/tmp}" - wrapper="${wrapper_root}/rustc-ubsan-wrapper" - cat > "${wrapper}" <> "$GITHUB_ENV" - echo "RUSTC_WORKSPACE_WRAPPER=" >> "$GITHUB_ENV" - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Clear sanitizer flags (musl) - shell: bash - run: | - set -euo pipefail - # Clear global Rust flags so host/proc-macro builds don't pull in UBSan. - echo "RUSTFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_ENCODED_RUSTFLAGS=" >> "$GITHUB_ENV" - echo "RUSTDOCFLAGS=" >> "$GITHUB_ENV" - # Override any runner-level Cargo config rustflags as well. - echo "CARGO_BUILD_RUSTFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUSTFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS=" >> "$GITHUB_ENV" - - sanitize_flags() { - local input="$1" - input="${input//-fsanitize=undefined/}" - input="${input//-fno-sanitize-recover=undefined/}" - input="${input//-fno-sanitize-trap=undefined/}" - echo "$input" - } - - cflags="$(sanitize_flags "${CFLAGS-}")" - cxxflags="$(sanitize_flags "${CXXFLAGS-}")" - echo "CFLAGS=${cflags}" >> "$GITHUB_ENV" - echo "CXXFLAGS=${cxxflags}" >> "$GITHUB_ENV" - - - if: ${{ !contains(matrix.target, 'windows') }} - name: Configure rusty_v8 artifact overrides and verify checksums - uses: ./.github/actions/setup-rusty-v8 - with: - target: ${{ matrix.target }} - - - name: Install cargo-chef - if: ${{ matrix.profile == 'release' }} - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: cargo-chef - version: 0.1.71 - - - name: Pre-warm dependency cache (cargo-chef) - if: ${{ matrix.profile == 'release' }} - shell: bash - run: | - set -euo pipefail - RECIPE="${RUNNER_TEMP}/chef-recipe.json" - cargo chef prepare --recipe-path "$RECIPE" - cargo chef cook --recipe-path "$RECIPE" --target ${{ matrix.target }} --release - - - name: cargo clippy - run: cargo clippy --target ${{ matrix.target }} --tests --profile ${{ matrix.profile }} --timings -- -D warnings - - - name: Upload Cargo timings (clippy) - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: cargo-timings-rust-ci-clippy-${{ matrix.target }}-${{ matrix.profile }} - path: codex-rs/target/**/cargo-timings/cargo-timing.html - if-no-files-found: warn - - # Save caches explicitly; make non-fatal so cache packaging - # never fails the overall job. Only save when key wasn't hit. - - name: Save cargo home cache - if: always() && !cancelled() && steps.cache_cargo_home_restore.outputs.cache-hit != 'true' - continue-on-error: true - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - ${{ github.workspace }}/.cargo-home/bin/ - ${{ github.workspace }}/.cargo-home/registry/index/ - ${{ github.workspace }}/.cargo-home/registry/cache/ - ${{ github.workspace }}/.cargo-home/git/db/ - key: cargo-home-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ steps.lockhash.outputs.toolchain_hash }} - - - name: Save sccache cache (fallback) - if: always() && !cancelled() && env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true' - continue-on-error: true - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: ${{ github.workspace }}/.sccache/ - key: sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }} - - - name: sccache stats - if: always() && env.USE_SCCACHE == 'true' - continue-on-error: true - run: sccache --show-stats || true - - - name: sccache summary - if: always() && env.USE_SCCACHE == 'true' - shell: bash - run: | - { - echo "### sccache stats — ${{ matrix.target }} (${{ matrix.profile }})"; - echo; - echo '```'; - sccache --show-stats || true; - echo '```'; - } >> "$GITHUB_STEP_SUMMARY" - - - name: Save APT cache (musl) - if: always() && !cancelled() && (matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl') && steps.cache_apt_restore.outputs.cache-hit != 'true' - continue-on-error: true - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - /var/cache/apt - key: apt-${{ matrix.runner }}-${{ matrix.target }}-v1 - - tests_macos_aarch64: - name: Tests — macos-15-xlarge - aarch64-apple-darwin - uses: ./.github/workflows/rust-ci-full-nextest-platform.yml - with: - runner: macos-15-xlarge - target: aarch64-apple-darwin - profile: ci-test - artifact_id: macos-aarch64 - use_sccache: true - secrets: inherit - - tests_linux_x64_remote: - name: Tests — ubuntu-24.04 - x86_64-unknown-linux-gnu (remote) - uses: ./.github/workflows/rust-ci-full-nextest-platform.yml - with: - runner: ubuntu-24.04 - runner_group: codex-runners - runner_labels: codex-linux-x64 - target: x86_64-unknown-linux-gnu - profile: ci-test - artifact_id: linux-x64-remote - remote_env: true - use_sccache: true - secrets: inherit - - tests_linux_arm64: - name: Tests — ubuntu-24.04-arm - aarch64-unknown-linux-gnu - uses: ./.github/workflows/rust-ci-full-nextest-platform.yml - with: - runner: ubuntu-24.04-arm - runner_group: codex-runners - runner_labels: codex-linux-arm64 - target: aarch64-unknown-linux-gnu - profile: ci-test - artifact_id: linux-arm64 - use_sccache: true - secrets: inherit - - tests_windows_x64: - name: Tests — windows-x64 - x86_64-pc-windows-msvc - uses: ./.github/workflows/rust-ci-full-nextest-platform.yml - with: - runner: windows-x64 - runner_group: codex-runners - runner_labels: codex-windows-x64 - target: x86_64-pc-windows-msvc - profile: ci-test - artifact_id: windows-x64 - test_threads: 8 - secrets: inherit - - tests_windows_arm64: - name: Tests — windows-arm64 - aarch64-pc-windows-msvc - uses: ./.github/workflows/rust-ci-full-nextest-platform.yml - with: - runner: windows-arm64 - runner_group: codex-runners - runner_labels: codex-windows-arm64 - archive_runner: windows-x64 - archive_runner_group: codex-runners - archive_runner_labels: codex-windows-x64 - target: aarch64-pc-windows-msvc - profile: ci-test - artifact_id: windows-arm64 - test_threads: 8 - use_sccache: true - secrets: inherit - - # --- Gatherer job for the full post-merge workflow -------------------------- - results: - name: Full CI results - needs: - [ - general, - cargo_shear, - argument_comment_lint_package, - argument_comment_lint_prebuilt, - lint_build, - tests_macos_aarch64, - tests_linux_x64_remote, - tests_linux_arm64, - tests_windows_x64, - tests_windows_arm64, - ] - if: always() - runs-on: ubuntu-24.04 - steps: - - name: Summarize - shell: bash - run: | - echo "argpkg : ${{ needs.argument_comment_lint_package.result }}" - echo "arglint: ${{ needs.argument_comment_lint_prebuilt.result }}" - echo "general: ${{ needs.general.result }}" - echo "shear : ${{ needs.cargo_shear.result }}" - echo "lint : ${{ needs.lint_build.result }}" - echo "test macos : ${{ needs.tests_macos_aarch64.result }}" - echo "test linux : ${{ needs.tests_linux_x64_remote.result }}" - echo "test arm64 : ${{ needs.tests_linux_arm64.result }}" - echo "test winx64: ${{ needs.tests_windows_x64.result }}" - echo "test winarm: ${{ needs.tests_windows_arm64.result }}" - [[ '${{ needs.argument_comment_lint_package.result }}' == 'success' ]] || { echo 'argument_comment_lint_package failed'; exit 1; } - [[ '${{ needs.argument_comment_lint_prebuilt.result }}' == 'success' ]] || { echo 'argument_comment_lint_prebuilt failed'; exit 1; } - [[ '${{ needs.general.result }}' == 'success' ]] || { echo 'general failed'; exit 1; } - [[ '${{ needs.cargo_shear.result }}' == 'success' ]] || { echo 'cargo_shear failed'; exit 1; } - [[ '${{ needs.lint_build.result }}' == 'success' ]] || { echo 'lint_build failed'; exit 1; } - [[ '${{ needs.tests_macos_aarch64.result }}' == 'success' ]] || { echo 'tests_macos_aarch64 failed'; exit 1; } - [[ '${{ needs.tests_linux_x64_remote.result }}' == 'success' ]] || { echo 'tests_linux_x64_remote failed'; exit 1; } - [[ '${{ needs.tests_linux_arm64.result }}' == 'success' ]] || { echo 'tests_linux_arm64 failed'; exit 1; } - [[ '${{ needs.tests_windows_x64.result }}' == 'success' ]] || { echo 'tests_windows_x64 failed'; exit 1; } - [[ '${{ needs.tests_windows_arm64.result }}' == 'success' ]] || { echo 'tests_windows_arm64 failed'; exit 1; } - - - name: sccache summary note - if: always() - run: | - echo "Per-job sccache stats are attached to each matrix job's Step Summary." diff --git a/reference/openai-codex/.github/workflows/rust-ci.yml b/reference/openai-codex/.github/workflows/rust-ci.yml deleted file mode 100644 index 029b6f2..0000000 --- a/reference/openai-codex/.github/workflows/rust-ci.yml +++ /dev/null @@ -1,245 +0,0 @@ -name: rust-ci -on: - pull_request: {} - workflow_dispatch: - -jobs: - # --- Detect what changed so the fast PR workflow only runs relevant jobs ---- - changed: - name: Detect changed areas - runs-on: ubuntu-24.04 - outputs: - argument_comment_lint: ${{ steps.detect.outputs.argument_comment_lint }} - argument_comment_lint_package: ${{ steps.detect.outputs.argument_comment_lint_package }} - codex: ${{ steps.detect.outputs.codex }} - workflows: ${{ steps.detect.outputs.workflows }} - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - fetch-depth: 0 - persist-credentials: false - - name: Detect changed paths (no external action) - id: detect - shell: bash - run: | - set -euo pipefail - - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - BASE_SHA='${{ github.event.pull_request.base.sha }}' - HEAD_SHA='${{ github.event.pull_request.head.sha }}' - echo "Base SHA: $BASE_SHA" - echo "Head SHA: $HEAD_SHA" - mapfile -t files < <(git diff --name-only --no-renames "$BASE_SHA" "$HEAD_SHA") - else - # On manual runs, default to the full fast-PR bundle. - files=("codex-rs/force" "tools/argument-comment-lint/force" ".github/force") - fi - - codex=false - argument_comment_lint=false - argument_comment_lint_package=false - workflows=false - for f in "${files[@]}"; do - [[ $f == codex-rs/* ]] && codex=true - [[ $f == codex-rs/* || $f == tools/argument-comment-lint/* || $f == justfile ]] && argument_comment_lint=true - [[ $f == defs.bzl || $f == workspace_root_test_launcher.sh.tpl || $f == workspace_root_test_launcher.bat.tpl ]] && argument_comment_lint=true - [[ $f == tools/argument-comment-lint/* || $f == .github/workflows/rust-ci.yml || $f == .github/workflows/rust-ci-full.yml ]] && argument_comment_lint_package=true - [[ $f == .github/* ]] && workflows=true - done - - echo "argument_comment_lint=$argument_comment_lint" >> "$GITHUB_OUTPUT" - echo "argument_comment_lint_package=$argument_comment_lint_package" >> "$GITHUB_OUTPUT" - echo "codex=$codex" >> "$GITHUB_OUTPUT" - echo "workflows=$workflows" >> "$GITHUB_OUTPUT" - - # --- Fast Cargo-native PR checks ------------------------------------------- - general: - name: Format / etc - runs-on: ubuntu-24.04 - needs: changed - if: ${{ needs.changed.outputs.codex == 'true' || needs.changed.outputs.workflows == 'true' }} - defaults: - run: - working-directory: codex-rs - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - components: rustfmt - - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: just - - name: cargo fmt - run: cargo fmt -- --config imports_granularity=Item --check - - name: Rust benchmark smoke test - run: just bench-smoke - - cargo_shear: - name: cargo shear - runs-on: ubuntu-24.04 - needs: changed - if: ${{ needs.changed.outputs.codex == 'true' || needs.changed.outputs.workflows == 'true' }} - defaults: - run: - working-directory: codex-rs - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: cargo-shear@1.11.2 - - name: cargo shear - run: cargo shear --deny-warnings - - argument_comment_lint_package: - name: Argument comment lint package - runs-on: ubuntu-24.04 - needs: changed - if: ${{ needs.changed.outputs.argument_comment_lint_package == 'true' }} - env: - CARGO_DYLINT_VERSION: 5.0.0 - DYLINT_LINK_VERSION: 5.0.0 - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - - name: Install nightly argument-comment-lint toolchain - shell: bash - run: | - rustup toolchain install nightly-2025-09-18 \ - --profile minimal \ - --component llvm-tools-preview \ - --component rustc-dev \ - --component rust-src \ - --no-self-update - rustup default nightly-2025-09-18 - - name: Cache cargo-dylint tooling - id: cargo_dylint_cache - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - ~/.cargo/bin/cargo-dylint - ~/.cargo/bin/dylint-link - ~/.cargo/registry/index - ~/.cargo/registry/cache - ~/.cargo/git/db - key: argument-comment-lint-${{ runner.os }}-${{ env.CARGO_DYLINT_VERSION }}-${{ env.DYLINT_LINK_VERSION }}-${{ hashFiles('tools/argument-comment-lint/Cargo.lock', 'tools/argument-comment-lint/rust-toolchain', '.github/workflows/rust-ci.yml', '.github/workflows/rust-ci-full.yml') }} - - name: Install cargo-dylint tooling - if: ${{ steps.cargo_dylint_cache.outputs.cache-hit != 'true' }} - shell: bash - run: | - cargo install --locked cargo-dylint --version "$CARGO_DYLINT_VERSION" - cargo install --locked dylint-link --version "$DYLINT_LINK_VERSION" - - name: Check Python wrapper syntax - run: python3 -m py_compile tools/argument-comment-lint/wrapper_common.py tools/argument-comment-lint/run.py tools/argument-comment-lint/run-prebuilt-linter.py tools/argument-comment-lint/test_wrapper_common.py - - name: Test Python wrapper helpers - run: python3 -m unittest discover -s tools/argument-comment-lint -p 'test_*.py' - - name: Test argument comment lint package - working-directory: tools/argument-comment-lint - run: cargo test - env: - RUST_MIN_STACK: "8388608" # 8 MiB - - argument_comment_lint_prebuilt: - name: Argument comment lint - ${{ matrix.name }} - runs-on: ${{ matrix.runs_on || matrix.runner }} - timeout-minutes: ${{ matrix.timeout_minutes }} - needs: changed - strategy: - fail-fast: false - matrix: - include: - - name: Linux - runner: ubuntu-24.04 - timeout_minutes: 30 - - name: macOS - runner: macos-15-xlarge - timeout_minutes: 30 - - name: Windows - runner: windows-x64 - timeout_minutes: 30 - runs_on: - group: codex-runners - labels: codex-windows-x64 - steps: - - name: Check whether argument comment lint should run - id: argument_comment_lint_gate - shell: bash - env: - ARGUMENT_COMMENT_LINT: ${{ needs.changed.outputs.argument_comment_lint }} - WORKFLOWS: ${{ needs.changed.outputs.workflows }} - run: | - if [[ "$ARGUMENT_COMMENT_LINT" == "true" || "$WORKFLOWS" == "true" ]]; then - echo "run=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - echo "No argument-comment-lint relevant changes." - echo "run=false" >> "$GITHUB_OUTPUT" - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - if: ${{ steps.argument_comment_lint_gate.outputs.run == 'true' }} - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - name: Run argument comment lint on codex-rs via Bazel - if: ${{ steps.argument_comment_lint_gate.outputs.run == 'true' }} - uses: ./.github/actions/run-argument-comment-lint - with: - target: ${{ runner.os }} - buildbuddy-api-key: ${{ secrets.BUILDBUDDY_API_KEY }} - - # --- Gatherer job that you mark as the ONLY required status ----------------- - results: - name: CI results (required) - needs: - [ - changed, - general, - cargo_shear, - argument_comment_lint_package, - argument_comment_lint_prebuilt, - ] - if: always() - runs-on: ubuntu-24.04 - steps: - - name: Summarize - shell: bash - run: | - echo "argpkg : ${{ needs.argument_comment_lint_package.result }}" - echo "arglint: ${{ needs.argument_comment_lint_prebuilt.result }}" - echo "general: ${{ needs.general.result }}" - echo "shear : ${{ needs.cargo_shear.result }}" - - # If nothing relevant changed (PR touching only root README, etc.), - # declare success regardless of other jobs. - if [[ "${NEEDS_CHANGED_OUTPUTS_ARGUMENT_COMMENT_LINT}" != 'true' && "${NEEDS_CHANGED_OUTPUTS_CODEX}" != 'true' && "${NEEDS_CHANGED_OUTPUTS_WORKFLOWS}" != 'true' ]]; then - echo 'No relevant changes -> CI not required.' - exit 0 - fi - - if [[ "${NEEDS_CHANGED_OUTPUTS_ARGUMENT_COMMENT_LINT_PACKAGE}" == 'true' ]]; then - [[ '${{ needs.argument_comment_lint_package.result }}' == 'success' ]] || { echo 'argument_comment_lint_package failed'; exit 1; } - fi - - if [[ "${NEEDS_CHANGED_OUTPUTS_ARGUMENT_COMMENT_LINT}" == 'true' || "${NEEDS_CHANGED_OUTPUTS_WORKFLOWS}" == 'true' ]]; then - [[ '${{ needs.argument_comment_lint_prebuilt.result }}' == 'success' ]] || { echo 'argument_comment_lint_prebuilt failed'; exit 1; } - fi - - if [[ "${NEEDS_CHANGED_OUTPUTS_CODEX}" == 'true' || "${NEEDS_CHANGED_OUTPUTS_WORKFLOWS}" == 'true' ]]; then - [[ '${{ needs.general.result }}' == 'success' ]] || { echo 'general failed'; exit 1; } - [[ '${{ needs.cargo_shear.result }}' == 'success' ]] || { echo 'cargo_shear failed'; exit 1; } - fi - env: - NEEDS_CHANGED_OUTPUTS_ARGUMENT_COMMENT_LINT: ${{ needs.changed.outputs.argument_comment_lint }} - NEEDS_CHANGED_OUTPUTS_CODEX: ${{ needs.changed.outputs.codex }} - NEEDS_CHANGED_OUTPUTS_WORKFLOWS: ${{ needs.changed.outputs.workflows }} - NEEDS_CHANGED_OUTPUTS_ARGUMENT_COMMENT_LINT_PACKAGE: ${{ needs.changed.outputs.argument_comment_lint_package }} diff --git a/reference/openai-codex/.github/workflows/rust-release-argument-comment-lint.yml b/reference/openai-codex/.github/workflows/rust-release-argument-comment-lint.yml deleted file mode 100644 index f654bd9..0000000 --- a/reference/openai-codex/.github/workflows/rust-release-argument-comment-lint.yml +++ /dev/null @@ -1,108 +0,0 @@ -name: rust-release-argument-comment-lint - -on: - workflow_call: - inputs: - publish: - required: true - type: boolean - -jobs: - skip: - if: ${{ !inputs.publish }} - runs-on: ubuntu-latest - steps: - - run: echo "Skipping argument-comment-lint release assets for prerelease tag" - - build: - if: ${{ inputs.publish }} - name: Build - ${{ matrix.runner }} - ${{ matrix.target }} - runs-on: ${{ matrix.runs_on || matrix.runner }} - timeout-minutes: 60 - env: - CARGO_DYLINT_VERSION: 5.0.0 - DYLINT_LINK_VERSION: 5.0.0 - - strategy: - fail-fast: false - matrix: - include: - - runner: macos-15-xlarge - target: aarch64-apple-darwin - archive_name: argument-comment-lint-aarch64-apple-darwin.tar.gz - lib_name: libargument_comment_lint@nightly-2025-09-18-aarch64-apple-darwin.dylib - runner_binary: argument-comment-lint - cargo_dylint_binary: cargo-dylint - - runner: ubuntu-24.04 - target: x86_64-unknown-linux-gnu - archive_name: argument-comment-lint-x86_64-unknown-linux-gnu.tar.gz - lib_name: libargument_comment_lint@nightly-2025-09-18-x86_64-unknown-linux-gnu.so - runner_binary: argument-comment-lint - cargo_dylint_binary: cargo-dylint - - runner: ubuntu-24.04-arm - target: aarch64-unknown-linux-gnu - archive_name: argument-comment-lint-aarch64-unknown-linux-gnu.tar.gz - lib_name: libargument_comment_lint@nightly-2025-09-18-aarch64-unknown-linux-gnu.so - runner_binary: argument-comment-lint - cargo_dylint_binary: cargo-dylint - - runner: windows-x64 - target: x86_64-pc-windows-msvc - archive_name: argument-comment-lint-x86_64-pc-windows-msvc.zip - lib_name: argument_comment_lint@nightly-2025-09-18-x86_64-pc-windows-msvc.dll - runner_binary: argument-comment-lint.exe - cargo_dylint_binary: cargo-dylint.exe - runs_on: - group: codex-runners - labels: codex-windows-x64 - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - toolchain: nightly-2025-09-18 - targets: ${{ matrix.target }} - components: llvm-tools-preview, rustc-dev, rust-src - - - name: Install tooling - shell: bash - run: | - install_root="${RUNNER_TEMP}/argument-comment-lint-tools" - cargo install --locked cargo-dylint --version "$CARGO_DYLINT_VERSION" --root "$install_root" - cargo install --locked dylint-link --version "$DYLINT_LINK_VERSION" - echo "INSTALL_ROOT=$install_root" >> "$GITHUB_ENV" - - - name: Cargo build - working-directory: tools/argument-comment-lint - shell: bash - run: cargo build --release --target ${{ matrix.target }} - - - name: Stage artifact - shell: bash - run: | - dest="dist/argument-comment-lint/${{ matrix.target }}" - mkdir -p "$dest" - package_root="${RUNNER_TEMP}/argument-comment-lint" - rm -rf "$package_root" - mkdir -p "$package_root/bin" "$package_root/lib" - - cp "tools/argument-comment-lint/target/${{ matrix.target }}/release/${{ matrix.runner_binary }}" \ - "$package_root/bin/${{ matrix.runner_binary }}" - cp "${INSTALL_ROOT}/bin/${{ matrix.cargo_dylint_binary }}" \ - "$package_root/bin/${{ matrix.cargo_dylint_binary }}" - cp "tools/argument-comment-lint/target/${{ matrix.target }}/release/${{ matrix.lib_name }}" \ - "$package_root/lib/${{ matrix.lib_name }}" - - archive_path="$dest/${{ matrix.archive_name }}" - if [[ "${{ runner.os }}" == "Windows" ]]; then - (cd "${RUNNER_TEMP}" && 7z a "$GITHUB_WORKSPACE/$archive_path" argument-comment-lint >/dev/null) - else - (cd "${RUNNER_TEMP}" && tar -czf "$GITHUB_WORKSPACE/$archive_path" argument-comment-lint) - fi - - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: argument-comment-lint-${{ matrix.target }} - path: dist/argument-comment-lint/${{ matrix.target }}/* diff --git a/reference/openai-codex/.github/workflows/rust-release-prepare.yml b/reference/openai-codex/.github/workflows/rust-release-prepare.yml deleted file mode 100644 index 67e542e..0000000 --- a/reference/openai-codex/.github/workflows/rust-release-prepare.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: rust-release-prepare -on: - workflow_dispatch: - schedule: - - cron: "0 */4 * * *" - -concurrency: - group: ${{ github.workflow }} - cancel-in-progress: false - -permissions: - contents: write - pull-requests: write - -jobs: - prepare: - # Prevent scheduled runs on forks (no secrets, wastes Actions minutes) - if: github.repository == 'openai/codex' - environment: - name: rust-release-prepare - deployment: false - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: main - fetch-depth: 0 - persist-credentials: false - - - name: Update models.json - env: - OPENAI_API_KEY: ${{ secrets.CODEX_OPENAI_API_KEY }} - run: | - set -euo pipefail - - client_version="99.99.99" - terminal_info="github-actions" - user_agent="codex_cli_rs/99.99.99 (Linux $(uname -r); $(uname -m)) ${terminal_info}" - base_url="${OPENAI_BASE_URL:-https://chatgpt.com/backend-api/codex}" - - headers=( - -H "Authorization: Bearer ${OPENAI_API_KEY}" - -H "User-Agent: ${user_agent}" - ) - - url="${base_url%/}/models?client_version=${client_version}" - curl --http1.1 --fail --show-error --location "${headers[@]}" "${url}" | jq '.' > codex-rs/models-manager/models.json - - - name: Open pull request (if changed) - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 - with: - commit-message: "Update models.json" - title: "Update models.json" - body: "Automated update of models.json." - branch: "bot/update-models-json" - reviewers: "pakrym-oai,aibrahim-oai" - delete-branch: true diff --git a/reference/openai-codex/.github/workflows/rust-release-windows.yml b/reference/openai-codex/.github/workflows/rust-release-windows.yml deleted file mode 100644 index 51412be..0000000 --- a/reference/openai-codex/.github/workflows/rust-release-windows.yml +++ /dev/null @@ -1,342 +0,0 @@ -name: rust-release-windows - -on: - workflow_call: - inputs: - release-lto: - required: true - type: string - secrets: - AZURE_TRUSTED_SIGNING_CLIENT_ID: - required: true - AZURE_TRUSTED_SIGNING_TENANT_ID: - required: true - AZURE_TRUSTED_SIGNING_SUBSCRIPTION_ID: - required: true - AZURE_TRUSTED_SIGNING_ENDPOINT: - required: true - AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: - required: true - AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: - required: true - -jobs: - build-windows-binaries: - name: Build Windows binaries - ${{ matrix.runner }} - ${{ matrix.target }} - ${{ matrix.bundle }} - runs-on: ${{ matrix.runs_on }} - # Windows release builds can exceed an hour on fat-LTO mainline releases, - # so keep the timeout aligned with the top-level release build headroom. - timeout-minutes: 90 - permissions: - contents: read - defaults: - run: - working-directory: codex-rs - env: - CARGO_PROFILE_RELEASE_LTO: ${{ inputs.release-lto }} - - strategy: - fail-fast: false - matrix: - include: - - runner: windows-x64 - target: x86_64-pc-windows-msvc - bundle: primary - binaries: "codex codex-responses-api-proxy" - runs_on: - group: codex-runners - labels: codex-windows-x64 - - runner: windows-arm64 - target: aarch64-pc-windows-msvc - bundle: primary - binaries: "codex codex-responses-api-proxy" - runs_on: - group: codex-runners - labels: codex-windows-arm64 - - runner: windows-x64 - target: x86_64-pc-windows-msvc - bundle: helpers - binaries: "codex-windows-sandbox-setup codex-command-runner" - runs_on: - group: codex-runners - labels: codex-windows-x64 - - runner: windows-arm64 - target: aarch64-pc-windows-msvc - bundle: helpers - binaries: "codex-windows-sandbox-setup codex-command-runner" - runs_on: - group: codex-runners - labels: codex-windows-arm64 - - runner: windows-x64 - target: x86_64-pc-windows-msvc - bundle: app-server - binaries: "codex-app-server" - runs_on: - group: codex-runners - labels: codex-windows-x64 - - runner: windows-arm64 - target: aarch64-pc-windows-msvc - bundle: app-server - binaries: "codex-app-server" - runs_on: - group: codex-runners - labels: codex-windows-arm64 - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: Print runner specs (Windows) - shell: powershell - run: | - $computer = Get-CimInstance Win32_ComputerSystem - $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 - $ramGiB = [math]::Round($computer.TotalPhysicalMemory / 1GB, 1) - Write-Host "Runner: $env:RUNNER_NAME" - Write-Host "OS: $([System.Environment]::OSVersion.VersionString)" - Write-Host "CPU: $($cpu.Name)" - Write-Host "Logical CPUs: $($computer.NumberOfLogicalProcessors)" - Write-Host "Physical CPUs: $($computer.NumberOfProcessors)" - Write-Host "Total RAM: $ramGiB GiB" - Write-Host "Disk usage:" - Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize Name, @{Name='Size(GB)';Expression={[math]::Round(($_.Used + $_.Free) / 1GB, 1)}}, @{Name='Free(GB)';Expression={[math]::Round($_.Free / 1GB, 1)}} - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - targets: ${{ matrix.target }} - - - name: Cargo build (Windows binaries) - shell: bash - run: | - build_args=() - for binary in ${{ matrix.binaries }}; do - build_args+=(--bin "$binary") - done - cargo build --target ${{ matrix.target }} --release --timings "${build_args[@]}" - - - name: Upload Cargo timings - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: cargo-timings-rust-release-windows-${{ matrix.target }}-${{ matrix.bundle }} - path: codex-rs/target/**/cargo-timings/cargo-timing.html - if-no-files-found: warn - - - name: Stage Windows binaries - shell: bash - run: | - output_dir="target/${{ matrix.target }}/release/staged-${{ matrix.bundle }}" - mkdir -p "$output_dir" - for binary in ${{ matrix.binaries }}; do - cp "target/${{ matrix.target }}/release/${binary}.exe" "$output_dir/${binary}.exe" - done - - - name: Upload Windows binaries - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: windows-binaries-${{ matrix.target }}-${{ matrix.bundle }} - path: | - codex-rs/target/${{ matrix.target }}/release/staged-${{ matrix.bundle }}/* - - build-windows: - needs: - - build-windows-binaries - name: Build - ${{ matrix.runner }} - ${{ matrix.target }} - runs-on: ${{ matrix.runs_on }} - timeout-minutes: 90 - permissions: - contents: read - id-token: write - defaults: - run: - working-directory: codex-rs - env: - WINDOWS_BINARIES: "codex codex-responses-api-proxy codex-windows-sandbox-setup codex-command-runner codex-app-server" - - strategy: - fail-fast: false - matrix: - include: - - runner: windows-x64 - target: x86_64-pc-windows-msvc - runs_on: - group: codex-runners - labels: codex-windows-x64 - - runner: windows-arm64 - target: aarch64-pc-windows-msvc - runs_on: - group: codex-runners - labels: codex-windows-arm64 - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Download prebuilt Windows primary binaries - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: windows-binaries-${{ matrix.target }}-primary - path: codex-rs/target/${{ matrix.target }}/release - - - name: Download prebuilt Windows helper binaries - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: windows-binaries-${{ matrix.target }}-helpers - path: codex-rs/target/${{ matrix.target }}/release - - - name: Download prebuilt Windows app-server binary - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: windows-binaries-${{ matrix.target }}-app-server - path: codex-rs/target/${{ matrix.target }}/release - - - name: Verify binaries - shell: bash - run: | - set -euo pipefail - for binary in ${WINDOWS_BINARIES}; do - ls -lh "target/${{ matrix.target }}/release/${binary}.exe" - done - - - name: Sign Windows binaries with Azure Trusted Signing - uses: ./.github/actions/windows-code-sign - with: - target: ${{ matrix.target }} - binaries: ${{ env.WINDOWS_BINARIES }} - client-id: ${{ secrets.AZURE_TRUSTED_SIGNING_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TRUSTED_SIGNING_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_TRUSTED_SIGNING_SUBSCRIPTION_ID }} - endpoint: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} - account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} - certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} - - - name: Stage artifacts - shell: bash - run: | - dest="dist/${{ matrix.target }}" - mkdir -p "$dest" - - for binary in ${WINDOWS_BINARIES}; do - cp "target/${{ matrix.target }}/release/${binary}.exe" \ - "$dest/${binary}-${{ matrix.target }}.exe" - done - - - name: Install DotSlash - uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2 - - - name: Build Codex package archives - shell: bash - run: | - set -euo pipefail - for bundle in primary app-server; do - bash "${GITHUB_WORKSPACE}/.github/scripts/build-codex-package-archive.sh" \ - --target "${{ matrix.target }}" \ - --bundle "$bundle" \ - --entrypoint-dir "target/${{ matrix.target }}/release" \ - --archive-dir "dist/${{ matrix.target }}" - done - - - name: Build Python runtime wheel - shell: bash - run: | - set -euo pipefail - - case "${{ matrix.target }}" in - aarch64-pc-windows-msvc) - platform_tag="win_arm64" - ;; - x86_64-pc-windows-msvc) - platform_tag="win_amd64" - ;; - *) - echo "No Python runtime wheel platform tag for ${{ matrix.target }}" - exit 1 - ;; - esac - - python -m venv "${RUNNER_TEMP}/python-runtime-build-venv" - "${RUNNER_TEMP}/python-runtime-build-venv/Scripts/python.exe" -m pip install build - - stage_dir="${RUNNER_TEMP}/openai-codex-cli-bin-${{ matrix.target }}" - wheel_dir="${GITHUB_WORKSPACE}/python-runtime-dist/${{ matrix.target }}" - python "${GITHUB_WORKSPACE}/sdk/python/scripts/update_sdk_artifacts.py" \ - stage-runtime \ - "$stage_dir" \ - "dist/${{ matrix.target }}/codex-package-${{ matrix.target }}.tar.gz" \ - --codex-version "${GITHUB_REF_NAME}" \ - --platform-tag "$platform_tag" - "${RUNNER_TEMP}/python-runtime-build-venv/Scripts/python.exe" -m build --wheel --outdir "$wheel_dir" "$stage_dir" - - - name: Upload Python runtime wheel - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: python-runtime-wheel-${{ matrix.target }} - path: python-runtime-dist/${{ matrix.target }}/*.whl - if-no-files-found: error - - - name: Compress artifacts - shell: bash - run: | - # Path that contains the uncompressed binaries for the current - # ${{ matrix.target }} - dest="dist/${{ matrix.target }}" - repo_root=$PWD - - # For compatibility with environments that lack the `zstd` tool we - # additionally create a `.tar.gz` and `.zip` for every Windows binary. - # The end result is: - # codex-.zst - # codex-.tar.gz - # codex-.zip - for f in "$dest"/*; do - base="$(basename "$f")" - # Skip files that are already archives (shouldn't happen, but be - # safe). - if [[ "$base" == *.tar.gz || "$base" == *.tar.zst || "$base" == *.zip || "$base" == *.dmg ]]; then - continue - fi - - # Don't try to compress signature bundles. - if [[ "$base" == *.sigstore ]]; then - continue - fi - - # Create per-binary tar.gz - tar -C "$dest" -czf "$dest/${base}.tar.gz" "$base" - - # Create zip archive for Windows binaries. - # Must run from inside the dest dir so 7z won't embed the - # directory path inside the zip. - if [[ "$base" == "codex-${{ matrix.target }}.exe" ]]; then - # Bundle the sandbox helper binaries into the main codex zip so - # WinGet installs include the required helpers next to codex.exe. - # Fall back to the single-binary zip if the helpers are missing - # to avoid breaking releases. - bundle_dir="$(mktemp -d)" - runner_src="$dest/codex-command-runner-${{ matrix.target }}.exe" - setup_src="$dest/codex-windows-sandbox-setup-${{ matrix.target }}.exe" - if [[ -f "$runner_src" && -f "$setup_src" ]]; then - cp "$dest/$base" "$bundle_dir/$base" - cp "$runner_src" "$bundle_dir/codex-command-runner.exe" - cp "$setup_src" "$bundle_dir/codex-windows-sandbox-setup.exe" - # Use an absolute path so bundle zips land in the real dist - # dir even when 7z runs from a temp directory. - (cd "$bundle_dir" && 7z a "$repo_root/$dest/${base}.zip" .) - else - echo "warning: missing sandbox binaries; falling back to single-binary zip" - echo "warning: expected $runner_src and $setup_src" - (cd "$dest" && 7z a "${base}.zip" "$base") - fi - rm -rf "$bundle_dir" - else - (cd "$dest" && 7z a "${base}.zip" "$base") - fi - - # Keep raw executables and produce .zst alongside them. - "${GITHUB_WORKSPACE}/.github/workflows/zstd" -T0 -19 "$dest/$base" - done - - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: ${{ matrix.target }} - path: | - codex-rs/dist/${{ matrix.target }}/* diff --git a/reference/openai-codex/.github/workflows/rust-release-zsh.yml b/reference/openai-codex/.github/workflows/rust-release-zsh.yml deleted file mode 100644 index b55d2e7..0000000 --- a/reference/openai-codex/.github/workflows/rust-release-zsh.yml +++ /dev/null @@ -1,103 +0,0 @@ -name: rust-release-zsh - -on: - workflow_call: - -env: - ZSH_COMMIT: 77045ef899e53b9598bebc5a41db93a548a40ca6 - ZSH_PATCH: codex-rs/shell-escalation/patches/zsh-exec-wrapper.patch - -jobs: - linux: - name: Build zsh (Linux) - ${{ matrix.variant }} - ${{ matrix.target }} - runs-on: ${{ matrix.runner }} - timeout-minutes: 30 - container: - image: ${{ matrix.image }} - - strategy: - fail-fast: false - matrix: - include: - - runner: ubuntu-24.04 - target: x86_64-unknown-linux-musl - variant: ubuntu-24.04 - image: ubuntu:24.04 - archive_name: codex-zsh-x86_64-unknown-linux-musl.tar.gz - - runner: ubuntu-24.04-arm - target: aarch64-unknown-linux-musl - variant: ubuntu-24.04 - image: arm64v8/ubuntu:24.04 - archive_name: codex-zsh-aarch64-unknown-linux-musl.tar.gz - - steps: - - name: Install build prerequisites - shell: bash - run: | - set -euo pipefail - apt-get update - DEBIAN_FRONTEND=noninteractive apt-get install -y \ - autoconf \ - bison \ - build-essential \ - ca-certificates \ - gettext \ - git \ - libncursesw5-dev - - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Build, smoke-test, and stage zsh artifact - shell: bash - run: | - "${GITHUB_WORKSPACE}/.github/scripts/build-zsh-release-artifact.sh" \ - "dist/zsh/${{ matrix.target }}/${{ matrix.archive_name }}" - - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: codex-zsh-${{ matrix.target }} - path: dist/zsh/${{ matrix.target }}/* - - darwin: - name: Build zsh (macOS) - ${{ matrix.variant }} - ${{ matrix.target }} - runs-on: ${{ matrix.runner }} - timeout-minutes: 30 - - strategy: - fail-fast: false - matrix: - include: - - runner: macos-15-large - target: x86_64-apple-darwin - variant: macos-15 - archive_name: codex-zsh-x86_64-apple-darwin.tar.gz - - runner: macos-15-xlarge - target: aarch64-apple-darwin - variant: macos-15 - archive_name: codex-zsh-aarch64-apple-darwin.tar.gz - - steps: - - name: Install build prerequisites - shell: bash - run: | - set -euo pipefail - if ! command -v autoconf >/dev/null 2>&1; then - brew install autoconf - fi - - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Build, smoke-test, and stage zsh artifact - shell: bash - run: | - "${GITHUB_WORKSPACE}/.github/scripts/build-zsh-release-artifact.sh" \ - "dist/zsh/${{ matrix.target }}/${{ matrix.archive_name }}" - - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: codex-zsh-${{ matrix.target }} - path: dist/zsh/${{ matrix.target }}/* diff --git a/reference/openai-codex/.github/workflows/rust-release.yml b/reference/openai-codex/.github/workflows/rust-release.yml deleted file mode 100644 index 2cd11e6..0000000 --- a/reference/openai-codex/.github/workflows/rust-release.yml +++ /dev/null @@ -1,1562 +0,0 @@ -# Release workflow for codex-rs. -# To release, follow a workflow like: -# ``` -# git tag -a rust-v0.1.0 -m "Release 0.1.0" -# git push origin rust-v0.1.0 -# ``` -# -# To use external macOS signing, manually dispatch `release_mode=build_unsigned`, -# sign the unsigned macOS artifacts in a secure enclave, upload the signed handoff -# archive as a GitHub Release asset, then manually dispatch -# `release_mode=promote_signed` with `unsigned_run_id` and `signed_macos_asset`. -# The signed handoff archive should contain target or artifact directories such -# as `aarch64-apple-darwin/` with signed binaries. - -name: rust-release -on: - push: - tags: - - "rust-v*.*.*" - workflow_dispatch: - inputs: - release_mode: - description: "build_unsigned creates unsigned macOS handoff artifacts; promote_signed finishes a release from signed macOS handoff artifacts." - required: false - type: choice - default: build_unsigned - options: - - build_unsigned - - promote_signed - sign_macos: - description: "Deprecated compatibility input; use release_mode instead." - required: false - type: boolean - default: false - unsigned_run_id: - description: "For promote_signed: workflow run id from the build_unsigned run." - required: false - type: string - signed_macos_asset: - description: "For promote_signed: exact GitHub Release asset name containing signed macOS handoff artifacts." - required: false - type: string - signed_macos_sha256: - description: "For promote_signed: optional SHA-256 of signed_macos_asset." - required: false - type: string - -concurrency: - group: ${{ github.workflow }} - cancel-in-progress: true - -jobs: - tag-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - - name: Validate tag matches Cargo.toml version - shell: bash - env: - RELEASE_MODE: ${{ github.event_name == 'workflow_dispatch' && inputs.release_mode || 'signed' }} - REQUESTED_SIGN_MACOS: ${{ inputs.sign_macos }} - SIGNED_MACOS_ASSET: ${{ inputs.signed_macos_asset }} - UNSIGNED_RUN_ID: ${{ inputs.unsigned_run_id }} - run: | - set -euo pipefail - echo "::group::Tag validation" - - case "${RELEASE_MODE}" in - signed) - if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then - echo "❌ Manual rust-release runs must use release_mode=build_unsigned or release_mode=promote_signed" - exit 1 - fi - ;; - build_unsigned) - if [[ "${GITHUB_EVENT_NAME}" != "workflow_dispatch" ]]; then - echo "❌ release_mode=build_unsigned is only valid for manual runs" - exit 1 - fi - ;; - promote_signed) - if [[ "${GITHUB_EVENT_NAME}" != "workflow_dispatch" ]]; then - echo "❌ release_mode=promote_signed is only valid for manual runs" - exit 1 - fi - if [[ ! "${UNSIGNED_RUN_ID}" =~ ^[0-9]+$ ]]; then - echo "❌ release_mode=promote_signed requires unsigned_run_id to be a workflow run id" - exit 1 - fi - if [[ -z "${SIGNED_MACOS_ASSET}" ]]; then - echo "❌ release_mode=promote_signed requires signed_macos_asset" - exit 1 - fi - if [[ "${SIGNED_MACOS_ASSET}" == */* || "${SIGNED_MACOS_ASSET}" == *"*"* || "${SIGNED_MACOS_ASSET}" == *"?"* || "${SIGNED_MACOS_ASSET}" == *"["* ]]; then - echo "❌ signed_macos_asset must be an exact release asset name, not a path or glob" - exit 1 - fi - if [[ "${UNSIGNED_RUN_ID}" == "${GITHUB_RUN_ID}" ]]; then - echo "❌ unsigned_run_id must refer to the earlier build_unsigned run, not this run" - exit 1 - fi - ;; - *) - echo "❌ Unknown release_mode '${RELEASE_MODE}'" - exit 1 - ;; - esac - - if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && "${REQUESTED_SIGN_MACOS}" == "true" ]]; then - echo "::warning title=Deprecated sign_macos input ignored::Use release_mode=build_unsigned or release_mode=promote_signed instead." - fi - - # 1. Must be a tag and match the regex - [[ "${GITHUB_REF_TYPE}" == "tag" ]] \ - || { echo "❌ Not a tag push"; exit 1; } - [[ "${GITHUB_REF_NAME}" =~ ^rust-v[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta)(\.[0-9]+)?)?$ ]] \ - || { echo "❌ Tag '${GITHUB_REF_NAME}' doesn't match expected format"; exit 1; } - - # 2. Extract versions - tag_ver="${GITHUB_REF_NAME#rust-v}" - cargo_ver="$(grep -m1 '^version' codex-rs/Cargo.toml \ - | sed -E 's/version *= *"([^"]+)".*/\1/')" - - # 3. Compare - [[ "${tag_ver}" == "${cargo_ver}" ]] \ - || { echo "❌ Tag ${tag_ver} ≠ Cargo.toml ${cargo_ver}"; exit 1; } - - echo "✅ Tag and Cargo.toml agree (${tag_ver})" - echo "::endgroup::" - - build: - if: ${{ github.event_name != 'workflow_dispatch' || inputs.release_mode != 'promote_signed' }} - needs: tag-check - name: Build - ${{ matrix.runner }} - ${{ matrix.target }} - ${{ matrix.bundle }} - runs-on: ${{ matrix.runs_on || matrix.runner }} - # Release builds can take a long time, so leave some headroom to avoid - # having to restart the full workflow due to a timeout. - timeout-minutes: 90 - permissions: - contents: read - id-token: write - defaults: - run: - working-directory: codex-rs - env: - # 2026-03-04: temporarily change releases to use thin LTO because - # Ubuntu ARM is timing out at 60 minutes. - CARGO_PROFILE_RELEASE_LTO: ${{ contains(github.ref_name, '-alpha') && 'thin' || 'thin' }} - SIGN_MACOS: ${{ github.event_name != 'workflow_dispatch' }} - - strategy: - fail-fast: false - matrix: - include: - - runner: macos-15-xlarge - target: aarch64-apple-darwin - bundle: primary - artifact_name: aarch64-apple-darwin - binaries: "codex codex-responses-api-proxy" - build_dmg: "true" - - runner: macos-15-xlarge - target: aarch64-apple-darwin - bundle: app-server - artifact_name: aarch64-apple-darwin-app-server - binaries: "codex-app-server" - build_dmg: "false" - - runner: macos-15-xlarge - target: x86_64-apple-darwin - bundle: primary - artifact_name: x86_64-apple-darwin - binaries: "codex codex-responses-api-proxy" - build_dmg: "true" - - runner: macos-15-xlarge - target: x86_64-apple-darwin - bundle: app-server - artifact_name: x86_64-apple-darwin-app-server - binaries: "codex-app-server" - build_dmg: "false" - # Release artifacts intentionally ship MUSL-linked Linux binaries. - - runner: codex-linux-x64-xl - target: x86_64-unknown-linux-musl - bundle: primary - artifact_name: x86_64-unknown-linux-musl - binaries: "codex codex-responses-api-proxy bwrap" - build_dmg: "false" - - runner: codex-linux-x64-xl - target: x86_64-unknown-linux-musl - bundle: app-server - artifact_name: x86_64-unknown-linux-musl-app-server - binaries: "codex-app-server" - build_dmg: "false" - - runner: codex-linux-arm64 - target: aarch64-unknown-linux-musl - bundle: primary - artifact_name: aarch64-unknown-linux-musl - binaries: "codex codex-responses-api-proxy bwrap" - build_dmg: "false" - - runner: codex-linux-arm64 - target: aarch64-unknown-linux-musl - bundle: app-server - artifact_name: aarch64-unknown-linux-musl-app-server - binaries: "codex-app-server" - build_dmg: "false" - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: Print runner specs (Linux) - if: ${{ runner.os == 'Linux' }} - shell: bash - run: | - set -euo pipefail - cpu_model="$(lscpu | awk -F: '/Model name/ {gsub(/^[ \t]+/, "", $2); print $2; exit}')" - total_ram="$(awk '/MemTotal/ {printf "%.1f GiB\n", $2 / 1024 / 1024}' /proc/meminfo)" - echo "Runner: ${RUNNER_NAME:-unknown}" - echo "OS: $(uname -a)" - echo "CPU model: ${cpu_model}" - echo "Logical CPUs: $(nproc)" - echo "Total RAM: ${total_ram}" - echo "Disk usage:" - df -h . - - name: Print runner specs (macOS) - if: ${{ runner.os == 'macOS' }} - shell: bash - run: | - set -euo pipefail - total_ram="$(sysctl -n hw.memsize | awk '{printf "%.1f GiB\n", $1 / 1024 / 1024 / 1024}')" - echo "Runner: ${RUNNER_NAME:-unknown}" - echo "OS: $(sw_vers -productName) $(sw_vers -productVersion)" - echo "Hardware model: $(sysctl -n hw.model)" - echo "CPU architecture: $(uname -m)" - echo "Logical CPUs: $(sysctl -n hw.logicalcpu)" - echo "Physical CPUs: $(sysctl -n hw.physicalcpu)" - echo "Total RAM: ${total_ram}" - echo "Disk usage:" - df -h . - - name: Install Linux bwrap build dependencies - if: ${{ runner.os == 'Linux' }} - shell: bash - run: | - set -euo pipefail - sudo apt-get update -y - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev - - name: Install UBSan runtime (musl) - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl' }} - shell: bash - run: | - set -euo pipefail - if command -v apt-get >/dev/null 2>&1; then - sudo apt-get update -y - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libubsan1 - fi - - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - targets: ${{ matrix.target }} - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Use hermetic Cargo home (musl) - shell: bash - run: | - set -euo pipefail - cargo_home="${GITHUB_WORKSPACE}/.cargo-home" - mkdir -p "${cargo_home}/bin" - echo "CARGO_HOME=${cargo_home}" >> "$GITHUB_ENV" - echo "${cargo_home}/bin" >> "$GITHUB_PATH" - : > "${cargo_home}/config.toml" - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Install Zig - uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 - with: - version: 0.14.0 - use-cache: false - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Install musl build tools - env: - TARGET: ${{ matrix.target }} - run: bash "${GITHUB_WORKSPACE}/.github/scripts/install-musl-build-tools.sh" - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Configure rustc UBSan wrapper (musl host) - shell: bash - run: | - set -euo pipefail - ubsan="" - if command -v ldconfig >/dev/null 2>&1; then - ubsan="$(ldconfig -p | grep -m1 'libubsan\.so\.1' | sed -E 's/.*=> (.*)$/\1/')" - fi - wrapper_root="${RUNNER_TEMP:-/tmp}" - wrapper="${wrapper_root}/rustc-ubsan-wrapper" - cat > "${wrapper}" <> "$GITHUB_ENV" - echo "RUSTC_WORKSPACE_WRAPPER=" >> "$GITHUB_ENV" - - - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} - name: Clear sanitizer flags (musl) - shell: bash - run: | - set -euo pipefail - # Avoid problematic aws-lc jitter entropy code path on musl builders. - echo "AWS_LC_SYS_NO_JITTER_ENTROPY=1" >> "$GITHUB_ENV" - target_no_jitter="AWS_LC_SYS_NO_JITTER_ENTROPY_${{ matrix.target }}" - target_no_jitter="${target_no_jitter//-/_}" - echo "${target_no_jitter}=1" >> "$GITHUB_ENV" - - # Clear global Rust flags so host/proc-macro builds don't pull in UBSan. - echo "RUSTFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_ENCODED_RUSTFLAGS=" >> "$GITHUB_ENV" - echo "RUSTDOCFLAGS=" >> "$GITHUB_ENV" - # Override any runner-level Cargo config rustflags as well. - echo "CARGO_BUILD_RUSTFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUSTFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS=" >> "$GITHUB_ENV" - - sanitize_flags() { - local input="$1" - input="${input//-fsanitize=undefined/}" - input="${input//-fno-sanitize-recover=undefined/}" - input="${input//-fno-sanitize-trap=undefined/}" - echo "$input" - } - - cflags="$(sanitize_flags "${CFLAGS-}")" - cxxflags="$(sanitize_flags "${CXXFLAGS-}")" - echo "CFLAGS=${cflags}" >> "$GITHUB_ENV" - echo "CXXFLAGS=${cxxflags}" >> "$GITHUB_ENV" - - - name: Configure rusty_v8 artifact overrides and verify checksums - uses: ./.github/actions/setup-rusty-v8 - with: - target: ${{ matrix.target }} - - - if: ${{ contains(matrix.target, 'linux') }} - name: Build bwrap and export digest - shell: bash - run: | - set -euo pipefail - target="${{ matrix.target }}" - cargo build --target "$target" --release --timings --bin bwrap - - bwrap_path="target/${target}/release/bwrap" - if [[ ! -f "$bwrap_path" ]]; then - echo "bwrap binary ${bwrap_path} not found" - exit 1 - fi - - digest="$(sha256sum "$bwrap_path" | awk '{print $1}')" - echo "CODEX_BWRAP_SHA256=${digest}" >> "$GITHUB_ENV" - echo "Built bwrap ${bwrap_path} with sha256:${digest}" - - - name: Cargo build - shell: bash - run: | - build_args=() - for binary in ${{ matrix.binaries }}; do - build_args+=(--bin "$binary") - done - echo "CARGO_PROFILE_RELEASE_LTO: ${CARGO_PROFILE_RELEASE_LTO}" - cargo build --target ${{ matrix.target }} --release --timings "${build_args[@]}" - - - name: Upload Cargo timings - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: cargo-timings-rust-release-${{ matrix.target }}-${{ matrix.bundle }} - path: codex-rs/target/**/cargo-timings/cargo-timing.html - if-no-files-found: warn - - - if: ${{ runner.os == 'macOS' && env.SIGN_MACOS != 'true' }} - name: Stage unsigned macOS artifacts - shell: bash - run: | - set -euo pipefail - - target="${{ matrix.target }}" - release_dir="target/${target}/release" - dest="unsigned-dist/${target}" - mkdir -p "$dest" - - for binary in ${{ matrix.binaries }}; do - binary_path="${release_dir}/${binary}" - unsigned_name="${binary}-${target}-unsigned" - unsigned_path="${dest}/${unsigned_name}" - if [[ ! -f "${binary_path}" ]]; then - echo "Binary ${binary_path} not found" - exit 1 - fi - - cp "${binary_path}" "${unsigned_path}" - tar -C "$dest" -czf "${unsigned_path}.tar.gz" "${unsigned_name}" - zstd -T0 -19 --rm "${unsigned_path}" - done - - - if: ${{ runner.os == 'macOS' && env.SIGN_MACOS != 'true' }} - name: Upload unsigned macOS artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: ${{ matrix.artifact_name }}-unsigned - path: codex-rs/unsigned-dist/${{ matrix.target }}/* - if-no-files-found: error - - - if: ${{ contains(matrix.target, 'linux') }} - name: Cosign Linux artifacts - uses: ./.github/actions/linux-code-sign - with: - target: ${{ matrix.target }} - artifacts-dir: ${{ github.workspace }}/codex-rs/target/${{ matrix.target }}/release - binaries: ${{ matrix.binaries }} - - - if: ${{ runner.os == 'macOS' && env.SIGN_MACOS == 'true' }} - name: MacOS code signing (binaries) - uses: ./.github/actions/macos-code-sign - with: - target: ${{ matrix.target }} - binaries: ${{ matrix.binaries }} - sign-binaries: "true" - sign-dmg: "false" - apple-certificate: ${{ secrets.APPLE_CERTIFICATE_P12 }} - apple-certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - apple-notarization-key-p8: ${{ secrets.APPLE_NOTARIZATION_KEY_P8 }} - apple-notarization-key-id: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }} - apple-notarization-issuer-id: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }} - - - if: ${{ runner.os == 'macOS' && matrix.build_dmg == 'true' && env.SIGN_MACOS == 'true' }} - name: Build macOS dmg - shell: bash - run: | - set -euo pipefail - - target="${{ matrix.target }}" - release_dir="target/${target}/release" - dmg_root="${RUNNER_TEMP}/codex-dmg-root" - volname="Codex (${target})" - dmg_path="${release_dir}/codex-${target}.dmg" - - # The previous "MacOS code signing (binaries)" step signs + notarizes the - # built artifacts in `${release_dir}`. This step packages *those same* - # signed binaries into a dmg. - rm -rf "$dmg_root" - mkdir -p "$dmg_root" - - for binary in ${{ matrix.binaries }}; do - binary_path="${release_dir}/${binary}" - if [[ ! -f "${binary_path}" ]]; then - echo "Binary ${binary_path} not found" - exit 1 - fi - ditto "${binary_path}" "${dmg_root}/${binary}" - done - - rm -f "$dmg_path" - hdiutil create \ - -volname "$volname" \ - -srcfolder "$dmg_root" \ - -format UDZO \ - -ov \ - "$dmg_path" - - if [[ ! -f "$dmg_path" ]]; then - echo "dmg $dmg_path not found after build" - exit 1 - fi - - - if: ${{ runner.os == 'macOS' && matrix.build_dmg == 'true' && env.SIGN_MACOS == 'true' }} - name: MacOS code signing (dmg) - uses: ./.github/actions/macos-code-sign - with: - target: ${{ matrix.target }} - sign-binaries: "false" - sign-dmg: "true" - apple-certificate: ${{ secrets.APPLE_CERTIFICATE_P12 }} - apple-certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - apple-notarization-key-p8: ${{ secrets.APPLE_NOTARIZATION_KEY_P8 }} - apple-notarization-key-id: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }} - apple-notarization-issuer-id: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }} - - - name: Stage artifacts - if: ${{ runner.os != 'macOS' || env.SIGN_MACOS == 'true' }} - shell: bash - run: | - dest="dist/${{ matrix.target }}" - mkdir -p "$dest" - - for binary in ${{ matrix.binaries }}; do - cp "target/${{ matrix.target }}/release/${binary}" "$dest/${binary}-${{ matrix.target }}" - if [[ "${{ matrix.target }}" == *linux* ]]; then - cp "target/${{ matrix.target }}/release/${binary}.sigstore" \ - "$dest/${binary}-${{ matrix.target }}.sigstore" - fi - done - - if [[ "${{ matrix.target }}" == *linux* && "${{ matrix.bundle }}" == "primary" ]]; then - bundle_root="${RUNNER_TEMP}/codex-${{ matrix.target }}-bundle" - rm -rf "$bundle_root" - mkdir -p "$bundle_root/codex-resources" - cp "$dest/codex-${{ matrix.target }}" "$bundle_root/codex" - cp "$dest/bwrap-${{ matrix.target }}" "$bundle_root/codex-resources/bwrap" - chmod 0755 "$bundle_root/codex" "$bundle_root/codex-resources/bwrap" - tar -C "$bundle_root" -cf - codex codex-resources/bwrap | - zstd -T0 -19 -o "$dest/codex-${{ matrix.target }}-bundle.tar.zst" - fi - - if [[ "${{ matrix.build_dmg }}" == "true" ]]; then - cp target/${{ matrix.target }}/release/codex-${{ matrix.target }}.dmg "$dest/codex-${{ matrix.target }}.dmg" - fi - - - name: Build Codex package archive - if: ${{ runner.os != 'macOS' || env.SIGN_MACOS == 'true' }} - shell: bash - env: - TARGET: ${{ matrix.target }} - BUNDLE: ${{ matrix.bundle }} - run: | - set -euo pipefail - bash "${GITHUB_WORKSPACE}/.github/scripts/build-codex-package-archive.sh" \ - --target "$TARGET" \ - --bundle "$BUNDLE" \ - --entrypoint-dir "target/${TARGET}/release" \ - --archive-dir "dist/${TARGET}" - - - name: Build Python runtime wheel - if: ${{ matrix.bundle == 'primary' && (runner.os != 'macOS' || env.SIGN_MACOS == 'true') }} - shell: bash - run: | - set -euo pipefail - - case "${{ matrix.target }}" in - aarch64-apple-darwin) - platform_tag="macosx_11_0_arm64" - ;; - x86_64-apple-darwin) - platform_tag="macosx_10_9_x86_64" - ;; - aarch64-unknown-linux-musl) - platform_tag="manylinux_2_17_aarch64" - ;; - x86_64-unknown-linux-musl) - platform_tag="manylinux_2_17_x86_64" - ;; - *) - echo "No Python runtime wheel platform tag for ${{ matrix.target }}" - exit 1 - ;; - esac - - python3 -m venv "${RUNNER_TEMP}/python-runtime-build-venv" - # Do not install into the runner's system Python; macOS runners mark - # the Homebrew Python as externally managed under PEP 668. - "${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m pip install build - - stage_dir="${RUNNER_TEMP}/openai-codex-cli-bin-${{ matrix.target }}" - wheel_dir="${GITHUB_WORKSPACE}/python-runtime-dist/${{ matrix.target }}" - stage_runtime_args=( - "${GITHUB_WORKSPACE}/sdk/python/scripts/update_sdk_artifacts.py" - stage-runtime - "$stage_dir" - "dist/${{ matrix.target }}/codex-package-${{ matrix.target }}.tar.gz" - --codex-version "${GITHUB_REF_NAME}" - --platform-tag "$platform_tag" - ) - python3 "${stage_runtime_args[@]}" - "${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m build --wheel --outdir "$wheel_dir" "$stage_dir" - - - name: Upload Python runtime wheel - if: ${{ matrix.bundle == 'primary' && (runner.os != 'macOS' || env.SIGN_MACOS == 'true') }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: python-runtime-wheel-${{ matrix.target }} - path: python-runtime-dist/${{ matrix.target }}/*.whl - if-no-files-found: error - - - name: Compress artifacts - if: ${{ runner.os != 'macOS' || env.SIGN_MACOS == 'true' }} - shell: bash - run: | - # Path that contains the uncompressed binaries for the current - # ${{ matrix.target }} - dest="dist/${{ matrix.target }}" - - # For compatibility with environments that lack the `zstd` tool we - # additionally create a `.tar.gz` alongside every binary we publish. - # The end result is: - # codex-.zst (existing) - # codex-.tar.gz (new) - - # 1. Produce a .tar.gz for every file in the directory *before* we - # run `zstd --rm`, because that flag deletes the original files. - for f in "$dest"/*; do - base="$(basename "$f")" - # Skip files that are already archives (shouldn't happen, but be - # safe). - if [[ "$base" == *.tar.gz || "$base" == *.tar.zst || "$base" == *.zip || "$base" == *.dmg ]]; then - continue - fi - - # Don't try to compress signature bundles. - if [[ "$base" == *.sigstore ]]; then - continue - fi - - # Create per-binary tar.gz - tar -C "$dest" -czf "$dest/${base}.tar.gz" "$base" - - # Also create .zst and remove the uncompressed binaries to keep - # non-Windows artifact directories small. - zstd -T0 -19 --rm "$dest/$base" - done - - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - if: ${{ runner.os != 'macOS' || env.SIGN_MACOS == 'true' }} - with: - name: ${{ matrix.artifact_name }} - # Upload the per-binary .zst files, .tar.gz equivalents, and any - # prebuilt archives staged above. - path: | - codex-rs/dist/${{ matrix.target }}/* - - stage-signed-macos: - if: ${{ github.event_name == 'workflow_dispatch' && inputs.release_mode == 'promote_signed' }} - needs: tag-check - name: Stage signed macOS handoff - ${{ matrix.target }} - ${{ matrix.bundle }} - runs-on: macos-15-xlarge - timeout-minutes: 30 - permissions: - contents: read - defaults: - run: - working-directory: codex-rs - - strategy: - fail-fast: false - matrix: - include: - - target: aarch64-apple-darwin - bundle: primary - artifact_name: aarch64-apple-darwin - binaries: "codex codex-responses-api-proxy" - build_dmg: "false" - - target: aarch64-apple-darwin - bundle: app-server - artifact_name: aarch64-apple-darwin-app-server - binaries: "codex-app-server" - build_dmg: "false" - - target: x86_64-apple-darwin - bundle: primary - artifact_name: x86_64-apple-darwin - binaries: "codex codex-responses-api-proxy" - build_dmg: "false" - - target: x86_64-apple-darwin - bundle: app-server - artifact_name: x86_64-apple-darwin-app-server - binaries: "codex-app-server" - build_dmg: "false" - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Download signed macOS handoff - shell: bash - env: - GH_TOKEN: ${{ github.token }} - SIGNED_MACOS_ASSET: ${{ inputs.signed_macos_asset }} - SIGNED_MACOS_SHA256: ${{ inputs.signed_macos_sha256 }} - run: | - set -euo pipefail - - download_dir="${RUNNER_TEMP}/signed-macos-download" - handoff_dir="${RUNNER_TEMP}/signed-macos-handoff" - rm -rf "$download_dir" "$handoff_dir" - mkdir -p "$download_dir" "$handoff_dir" - - gh release download "$GITHUB_REF_NAME" \ - --repo "$GITHUB_REPOSITORY" \ - --pattern "$SIGNED_MACOS_ASSET" \ - --dir "$download_dir" - - asset_count="$(find "$download_dir" -maxdepth 1 -type f | wc -l | tr -d '[:space:]')" - if [[ "$asset_count" != "1" ]]; then - echo "Expected exactly one signed macOS handoff asset named ${SIGNED_MACOS_ASSET}; found ${asset_count}" - find "$download_dir" -maxdepth 1 -type f -print - exit 1 - fi - - asset_path="$(find "$download_dir" -maxdepth 1 -type f -print -quit)" - if [[ -n "${SIGNED_MACOS_SHA256}" ]]; then - expected_sha="$(printf '%s' "$SIGNED_MACOS_SHA256" | tr '[:upper:]' '[:lower:]')" - actual_sha="$(shasum -a 256 "$asset_path" | awk '{print $1}')" - if [[ "$actual_sha" != "$expected_sha" ]]; then - echo "signed_macos_sha256 mismatch for ${SIGNED_MACOS_ASSET}" - echo "expected: ${expected_sha}" - echo "actual: ${actual_sha}" - exit 1 - fi - fi - - asset_name="$(basename "$asset_path")" - case "$asset_name" in - *.tar.zst) - zstd -dc "$asset_path" | tar -C "$handoff_dir" -xf - - ;; - *.tar.gz|*.tgz) - tar -C "$handoff_dir" -xzf "$asset_path" - ;; - *.zip) - ditto -x -k "$asset_path" "$handoff_dir" - ;; - *) - echo "Unsupported signed macOS handoff archive format: ${asset_name}" - exit 1 - ;; - esac - - echo "SIGNED_MACOS_HANDOFF_DIR=$handoff_dir" >> "$GITHUB_ENV" - - - name: Stage signed macOS artifacts - shell: bash - run: | - set -euo pipefail - - target="${{ matrix.target }}" - artifact_name="${{ matrix.artifact_name }}" - source_dir="${SIGNED_MACOS_HANDOFF_DIR}/${artifact_name}" - if [[ ! -d "$source_dir" && -d "${SIGNED_MACOS_HANDOFF_DIR}/dist/${artifact_name}" ]]; then - source_dir="${SIGNED_MACOS_HANDOFF_DIR}/dist/${artifact_name}" - fi - if [[ ! -d "$source_dir" && -d "${SIGNED_MACOS_HANDOFF_DIR}/${target}" ]]; then - source_dir="${SIGNED_MACOS_HANDOFF_DIR}/${target}" - fi - if [[ ! -d "$source_dir" && -d "${SIGNED_MACOS_HANDOFF_DIR}/dist/${target}" ]]; then - source_dir="${SIGNED_MACOS_HANDOFF_DIR}/dist/${target}" - fi - if [[ ! -d "$source_dir" ]]; then - echo "Signed macOS handoff is missing ${artifact_name}/" - echo "Expected either:" - echo " ${SIGNED_MACOS_HANDOFF_DIR}/${artifact_name}" - echo " ${SIGNED_MACOS_HANDOFF_DIR}/dist/${artifact_name}" - echo " ${SIGNED_MACOS_HANDOFF_DIR}/${target}" - echo " ${SIGNED_MACOS_HANDOFF_DIR}/dist/${target}" - find "$SIGNED_MACOS_HANDOFF_DIR" -maxdepth 3 -type f -print - exit 1 - fi - - dest="dist/${target}" - mkdir -p "$dest" - - for binary in ${{ matrix.binaries }}; do - source_path="${source_dir}/${binary}" - if [[ ! -f "$source_path" ]]; then - source_path="${source_dir}/${binary}-${target}" - fi - if [[ ! -f "$source_path" ]]; then - echo "Signed macOS handoff is missing ${binary} for ${artifact_name}" - exit 1 - fi - - release_path="${dest}/${binary}-${target}" - ditto "$source_path" "$release_path" - chmod 0755 "$release_path" - codesign --verify --strict --verbose=2 "$release_path" - done - - # DMG staging is disabled for signed promotion because we no longer - # distribute DMGs from this release path. Keep the branch here so the - # handoff can opt back in by flipping matrix.build_dmg if needed. - if [[ "${{ matrix.build_dmg }}" == "true" ]]; then - dmg_name="codex-${target}.dmg" - dmg_source="${source_dir}/${dmg_name}" - if [[ ! -f "$dmg_source" ]]; then - echo "Signed macOS handoff is missing ${dmg_name} for ${artifact_name}" - exit 1 - fi - - codesign --verify --strict --verbose=2 "$dmg_source" - xcrun stapler validate "$dmg_source" - cp "$dmg_source" "$dest/$dmg_name" - fi - - - name: Build Codex package archive - shell: bash - env: - TARGET: ${{ matrix.target }} - BUNDLE: ${{ matrix.bundle }} - run: | - set -euo pipefail - bash "${GITHUB_WORKSPACE}/.github/scripts/build-codex-package-archive.sh" \ - --target "$TARGET" \ - --bundle "$BUNDLE" \ - --entrypoint-dir "dist/${TARGET}" \ - --archive-dir "dist/${TARGET}" \ - --target-suffixed-entrypoint - - - name: Build Python runtime wheel - if: ${{ matrix.bundle == 'primary' }} - shell: bash - run: | - set -euo pipefail - - case "${{ matrix.target }}" in - aarch64-apple-darwin) - platform_tag="macosx_11_0_arm64" - ;; - x86_64-apple-darwin) - platform_tag="macosx_10_9_x86_64" - ;; - *) - echo "No Python runtime wheel platform tag for ${{ matrix.target }}" - exit 1 - ;; - esac - - python3 -m venv "${RUNNER_TEMP}/python-runtime-build-venv" - "${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m pip install build - - stage_dir="${RUNNER_TEMP}/openai-codex-cli-bin-${{ matrix.target }}" - wheel_dir="${GITHUB_WORKSPACE}/python-runtime-dist/${{ matrix.target }}" - python3 \ - "${GITHUB_WORKSPACE}/sdk/python/scripts/update_sdk_artifacts.py" \ - stage-runtime \ - "$stage_dir" \ - "dist/${{ matrix.target }}/codex-package-${{ matrix.target }}.tar.gz" \ - --codex-version "${GITHUB_REF_NAME}" \ - --platform-tag "$platform_tag" - "${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m build --wheel --outdir "$wheel_dir" "$stage_dir" - - - name: Upload Python runtime wheel - if: ${{ matrix.bundle == 'primary' }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: python-runtime-wheel-${{ matrix.target }} - path: python-runtime-dist/${{ matrix.target }}/*.whl - if-no-files-found: error - - - name: Compress artifacts - shell: bash - run: | - set -euo pipefail - - dest="dist/${{ matrix.target }}" - for f in "$dest"/*; do - base="$(basename "$f")" - if [[ "$base" == *.tar.gz || "$base" == *.tar.zst || "$base" == *.zip || "$base" == *.dmg ]]; then - continue - fi - - tar -C "$dest" -czf "$dest/${base}.tar.gz" "$base" - zstd -T0 -19 --rm "$dest/$base" - done - - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: ${{ matrix.artifact_name }} - path: | - codex-rs/dist/${{ matrix.target }}/* - - build-windows: - if: ${{ github.event_name != 'workflow_dispatch' || inputs.release_mode != 'promote_signed' }} - needs: tag-check - uses: ./.github/workflows/rust-release-windows.yml - with: - release-lto: ${{ contains(github.ref_name, '-alpha') && 'thin' || 'fat' }} - secrets: inherit - - argument-comment-lint-release-assets: - if: ${{ github.event_name != 'workflow_dispatch' || inputs.release_mode != 'promote_signed' }} - name: argument-comment-lint release assets - needs: tag-check - uses: ./.github/workflows/rust-release-argument-comment-lint.yml - with: - publish: true - - zsh-release-assets: - if: ${{ github.event_name != 'workflow_dispatch' || inputs.release_mode != 'promote_signed' }} - name: zsh release assets - needs: tag-check - uses: ./.github/workflows/rust-release-zsh.yml - - release: - needs: - - tag-check - - build - - stage-signed-macos - - build-windows - - argument-comment-lint-release-assets - - zsh-release-assets - if: >- - ${{ - always() && - needs.tag-check.result == 'success' && - ( - ( - github.event_name == 'workflow_dispatch' && - inputs.release_mode == 'promote_signed' && - needs.stage-signed-macos.result == 'success' && - needs.build.result == 'skipped' && - needs.build-windows.result == 'skipped' && - needs.argument-comment-lint-release-assets.result == 'skipped' && - needs.zsh-release-assets.result == 'skipped' - ) || - ( - (github.event_name != 'workflow_dispatch' || inputs.release_mode != 'promote_signed') && - needs.build.result == 'success' && - needs.stage-signed-macos.result == 'skipped' && - needs.build-windows.result == 'success' && - needs.argument-comment-lint-release-assets.result == 'success' && - needs.zsh-release-assets.result == 'success' - ) - ) - }} - name: release - runs-on: ubuntu-latest - permissions: - contents: write - actions: read - env: - RELEASE_MODE: ${{ github.event_name == 'workflow_dispatch' && inputs.release_mode || 'signed' }} - SIGN_MACOS: ${{ github.event_name != 'workflow_dispatch' || inputs.release_mode == 'promote_signed' }} - SIGNED_MACOS_ASSET: ${{ inputs.signed_macos_asset }} - UNSIGNED_RUN_ID: ${{ inputs.unsigned_run_id }} - outputs: - version: ${{ steps.release_name.outputs.name }} - tag: ${{ github.ref_name }} - sign_macos: ${{ steps.release_mode.outputs.sign_macos }} - should_publish_npm: ${{ steps.npm_publish_settings.outputs.should_publish }} - npm_tag: ${{ steps.npm_publish_settings.outputs.npm_tag }} - should_publish_python_runtime: ${{ steps.python_runtime_publish_settings.outputs.should_publish }} - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Define release mode - id: release_mode - run: | - echo "release_mode=${RELEASE_MODE}" >> "$GITHUB_OUTPUT" - echo "sign_macos=${SIGN_MACOS}" >> "$GITHUB_OUTPUT" - - - name: Generate release notes from tag commit message - id: release_notes - shell: bash - run: | - set -euo pipefail - - # On tag pushes, GITHUB_SHA may be a tag object for annotated tags; - # peel it to the underlying commit. - commit="$(git rev-parse "${GITHUB_SHA}^{commit}")" - notes_path="${RUNNER_TEMP}/release-notes.md" - - # Use the commit message for the commit the tag points at (not the - # annotated tag message). - git log -1 --format=%B "${commit}" > "${notes_path}" - # Ensure trailing newline so GitHub's markdown renderer doesn't - # occasionally run the last line into subsequent content. - echo >> "${notes_path}" - - echo "path=${notes_path}" >> "${GITHUB_OUTPUT}" - - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - path: dist - - - name: Validate unsigned build run - if: ${{ env.RELEASE_MODE == 'promote_signed' }} - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - - run_summary="$(gh run view "$UNSIGNED_RUN_ID" \ - --repo "$GITHUB_REPOSITORY" \ - --json conclusion,event,headBranch,headSha,status,workflowName,url \ - --jq '[.workflowName, .event, .headBranch, .headSha, .status, .conclusion, .url] | @tsv')" - IFS=$'\t' read -r workflow_name event head_branch head_sha status conclusion run_url <<< "$run_summary" - expected_head_sha="$(git rev-parse "${GITHUB_SHA}^{commit}")" - - if [[ "$workflow_name" != "$GITHUB_WORKFLOW" ]]; then - echo "unsigned_run_id ${UNSIGNED_RUN_ID} is for workflow '${workflow_name}', expected '${GITHUB_WORKFLOW}'" - echo "Run URL: ${run_url}" - exit 1 - fi - - if [[ "$event" != "workflow_dispatch" ]]; then - echo "unsigned_run_id ${UNSIGNED_RUN_ID} was triggered by '${event}', expected 'workflow_dispatch'" - echo "Run URL: ${run_url}" - exit 1 - fi - - if [[ "$head_branch" != "$GITHUB_REF_NAME" ]]; then - echo "unsigned_run_id ${UNSIGNED_RUN_ID} used ref '${head_branch}', expected '${GITHUB_REF_NAME}'" - echo "Run URL: ${run_url}" - exit 1 - fi - - if [[ "$head_sha" != "$expected_head_sha" ]]; then - echo "unsigned_run_id ${UNSIGNED_RUN_ID} used head SHA '${head_sha}', expected '${expected_head_sha}'" - echo "Run URL: ${run_url}" - exit 1 - fi - - if [[ "$status" != "completed" || "$conclusion" != "success" ]]; then - echo "unsigned_run_id ${UNSIGNED_RUN_ID} is ${status}/${conclusion}, expected completed/success" - echo "Run URL: ${run_url}" - exit 1 - fi - - - name: Download artifacts from unsigned build run - if: ${{ env.RELEASE_MODE == 'promote_signed' }} - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - gh run download "$UNSIGNED_RUN_ID" \ - --repo "$GITHUB_REPOSITORY" \ - --dir dist - - - name: Remove unsigned macOS staging artifacts - if: ${{ env.RELEASE_MODE == 'promote_signed' }} - run: | - set -euo pipefail - find dist -mindepth 1 -maxdepth 1 -type d \ - -name '*-apple-darwin*-unsigned' \ - -exec rm -rf {} + - - - name: Re-upload promoted Linux x64 artifacts - if: ${{ env.RELEASE_MODE == 'promote_signed' }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: x86_64-unknown-linux-musl - path: dist/x86_64-unknown-linux-musl/* - if-no-files-found: error - - - name: Re-upload promoted Linux arm64 artifacts - if: ${{ env.RELEASE_MODE == 'promote_signed' }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: aarch64-unknown-linux-musl - path: dist/aarch64-unknown-linux-musl/* - if-no-files-found: error - - - name: Re-upload promoted Windows x64 artifacts - if: ${{ env.RELEASE_MODE == 'promote_signed' }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: x86_64-pc-windows-msvc - path: dist/x86_64-pc-windows-msvc/* - if-no-files-found: error - - - name: Re-upload promoted Windows arm64 artifacts - if: ${{ env.RELEASE_MODE == 'promote_signed' }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: aarch64-pc-windows-msvc - path: dist/aarch64-pc-windows-msvc/* - if-no-files-found: error - - - name: List - run: ls -R dist/ - - - name: Prune artifacts excluded from unsigned macOS release - if: ${{ env.SIGN_MACOS == 'false' }} - run: | - find dist -mindepth 1 -maxdepth 1 -type d \ - ! -name '*-apple-darwin*-unsigned' \ - ! -name 'aarch64-unknown-linux-musl' \ - ! -name 'aarch64-unknown-linux-musl-app-server' \ - ! -name 'x86_64-unknown-linux-musl' \ - ! -name 'x86_64-unknown-linux-musl-app-server' \ - ! -name 'aarch64-pc-windows-msvc' \ - ! -name 'x86_64-pc-windows-msvc' \ - -exec rm -rf {} + - - if ! find dist -type f -name '*-apple-darwin*-unsigned*' | grep -q .; then - echo "No unsigned macOS artifacts found in downloaded workflow artifacts." - exit 1 - fi - - - name: Delete entries from dist/ that should not go in the release - run: | - rm -rf dist/windows-binaries* - # cargo-timing.html appears under multiple target-specific directories. - # If included in files: dist/**, release upload races on duplicate - # asset names and can fail with 404s. - find dist -type f -name 'cargo-timing.html' -delete - find dist -type d -empty -delete - - ls -R dist/ - - - name: Add Codex package checksum manifest - run: | - set -euo pipefail - - manifest="dist/codex-package_SHA256SUMS" - tmp_manifest="$(mktemp)" - find dist -type f \ - \( -name 'codex-package-*.tar.gz' -o -name 'codex-app-server-package-*.tar.gz' \) \ - -print | - sort | - while IFS= read -r archive; do - sha256sum "$archive" | - awk -v name="$(basename "$archive")" '{ print $1 " " name }' - done > "$tmp_manifest" - - if [[ ! -s "$tmp_manifest" ]]; then - echo "No Codex package archives found for checksum manifest" - exit 1 - fi - - mv "$tmp_manifest" "$manifest" - cat "$manifest" - - - name: Add config schema release asset - run: | - cp codex-rs/core/config.schema.json dist/config-schema.json - - - name: Define release name - id: release_name - run: | - # Extract the version from the tag name, which is in the format - # "rust-v0.1.0". - version="${GITHUB_REF_NAME#rust-v}" - echo "name=${version}" >> $GITHUB_OUTPUT - - - name: Determine npm publish settings - id: npm_publish_settings - env: - VERSION: ${{ steps.release_name.outputs.name }} - run: | - set -euo pipefail - version="${VERSION}" - - if [[ "${SIGN_MACOS}" != "true" ]]; then - echo "should_publish=false" >> "$GITHUB_OUTPUT" - echo "npm_tag=" >> "$GITHUB_OUTPUT" - exit 0 - fi - - if [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "should_publish=true" >> "$GITHUB_OUTPUT" - echo "npm_tag=" >> "$GITHUB_OUTPUT" - elif [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+-alpha\.[0-9]+$ ]]; then - echo "should_publish=true" >> "$GITHUB_OUTPUT" - echo "npm_tag=alpha" >> "$GITHUB_OUTPUT" - else - echo "should_publish=false" >> "$GITHUB_OUTPUT" - echo "npm_tag=" >> "$GITHUB_OUTPUT" - fi - - - name: Determine Python runtime publish settings - id: python_runtime_publish_settings - env: - VERSION: ${{ steps.release_name.outputs.name }} - run: | - set -euo pipefail - version="${VERSION}" - - if [[ "${SIGN_MACOS}" != "true" ]]; then - echo "should_publish=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - if [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "should_publish=true" >> "$GITHUB_OUTPUT" - elif [[ "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+-alpha\.[0-9]+$ ]]; then - echo "should_publish=true" >> "$GITHUB_OUTPUT" - else - echo "should_publish=false" >> "$GITHUB_OUTPUT" - fi - - - name: Setup pnpm - if: ${{ env.SIGN_MACOS == 'true' }} - uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5 - with: - run_install: false - - - name: Setup Node.js for npm packaging - if: ${{ env.SIGN_MACOS == 'true' }} - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - with: - node-version: 22 - - - name: Install dependencies - if: ${{ env.SIGN_MACOS == 'true' }} - run: pnpm install --frozen-lockfile - - - name: Stage npm packages - if: ${{ env.SIGN_MACOS == 'true' }} - env: - GH_TOKEN: ${{ github.token }} - RELEASE_VERSION: ${{ steps.release_name.outputs.name }} - run: | - workflow_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" - ./scripts/stage_npm_packages.py \ - --release-version "$RELEASE_VERSION" \ - --workflow-url "$workflow_url" \ - --package codex \ - --package codex-responses-api-proxy \ - --package codex-sdk - - - name: Stage installer scripts - if: ${{ env.SIGN_MACOS == 'true' }} - run: | - cp scripts/install/install.sh dist/install.sh - cp scripts/install/install.ps1 dist/install.ps1 - - - name: Create GitHub Release - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 - with: - name: ${{ steps.release_name.outputs.name }} - tag_name: ${{ github.ref_name }} - body_path: ${{ steps.release_notes.outputs.path }} - files: dist/** - overwrite_files: true - make_latest: ${{ env.SIGN_MACOS == 'true' && !contains(steps.release_name.outputs.name, '-') }} - # Mark as prerelease only when the version has a suffix after x.y.z - # (e.g. -alpha, -beta). Otherwise publish a normal release. - prerelease: ${{ contains(steps.release_name.outputs.name, '-') }} - - - name: Clean up signed promotion handoff assets - if: ${{ env.RELEASE_MODE == 'promote_signed' }} - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - - release_id="$(gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${GITHUB_REF_NAME}" --jq '.id')" - gh api --paginate "repos/${GITHUB_REPOSITORY}/releases/${release_id}/assets" \ - --jq '.[] | [.id, .name] | @tsv' | - while IFS=$'\t' read -r asset_id asset_name; do - if [[ -z "$asset_id" || -z "$asset_name" ]]; then - continue - fi - - delete_asset=false - if [[ "$asset_name" == *unsigned* || "$asset_name" == "$SIGNED_MACOS_ASSET" ]]; then - delete_asset=true - fi - - if [[ "$delete_asset" == "true" ]]; then - echo "Deleting release asset ${asset_name}" - gh api -X DELETE "repos/${GITHUB_REPOSITORY}/releases/assets/${asset_id}" - fi - done - - - if: ${{ env.SIGN_MACOS == 'true' }} - uses: facebook/dotslash-publish-release@9c9ec027515c34db9282a09a25a9cab5880b2c52 # v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag: ${{ github.ref_name }} - config: .github/dotslash-config.json - - - if: ${{ env.SIGN_MACOS == 'true' }} - uses: facebook/dotslash-publish-release@9c9ec027515c34db9282a09a25a9cab5880b2c52 # v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag: ${{ github.ref_name }} - config: .github/dotslash-zsh-config.json - - - if: ${{ env.SIGN_MACOS == 'true' }} - uses: facebook/dotslash-publish-release@9c9ec027515c34db9282a09a25a9cab5880b2c52 # v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag: ${{ github.ref_name }} - config: .github/dotslash-argument-comment-lint-config.json - - - name: Trigger developers.openai.com deploy - # Only trigger the deploy if the release is not a pre-release. - # The deploy is used to update the developers.openai.com website with the new config schema json file. - if: ${{ env.SIGN_MACOS == 'true' && !contains(steps.release_name.outputs.name, '-') }} - continue-on-error: true - env: - DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL: ${{ secrets.DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL }} - run: | - if ! curl -sS -f -o /dev/null -X POST "$DEV_WEBSITE_VERCEL_DEPLOY_HOOK_URL"; then - echo "::warning title=developers.openai.com deploy hook failed::Vercel deploy hook POST failed for ${GITHUB_REF_NAME}" - exit 1 - fi - - # Publish to npm using OIDC authentication. - # July 31, 2025: https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/ - # npm docs: https://docs.npmjs.com/trusted-publishers - publish-npm: - # Publish to npm for stable releases and alpha pre-releases with numeric suffixes. - # promote_signed intentionally skips build jobs that are ancestors of release; - # include the !cancelled() status function so Actions does not apply its implicit - # success() check to the whole dependency chain before evaluating release outputs. - if: >- - ${{ - !cancelled() && - needs.release.result == 'success' && - needs.release.outputs.should_publish_npm == 'true' - }} - name: publish-npm - needs: release - runs-on: ubuntu-latest - permissions: - id-token: write # Required for OIDC - contents: read - - steps: - - name: Setup Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - with: - # Node 24 bundles npm >= 11.5.1, which trusted publishing requires. - node-version: 24 - registry-url: "https://registry.npmjs.org" - scope: "@openai" - - - name: Download npm tarballs from release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - RELEASE_TAG: ${{ needs.release.outputs.tag }} - RELEASE_VERSION: ${{ needs.release.outputs.version }} - run: | - set -euo pipefail - version="$RELEASE_VERSION" - tag="$RELEASE_TAG" - mkdir -p dist/npm - patterns=( - "codex-npm-${version}.tgz" - "codex-npm-linux-*-${version}.tgz" - "codex-npm-darwin-*-${version}.tgz" - "codex-npm-win32-*-${version}.tgz" - "codex-responses-api-proxy-npm-${version}.tgz" - "codex-sdk-npm-${version}.tgz" - ) - for pattern in "${patterns[@]}"; do - gh release download "$tag" \ - --repo "${GITHUB_REPOSITORY}" \ - --pattern "$pattern" \ - --dir dist/npm - done - - # No NODE_AUTH_TOKEN needed because we use OIDC. - - name: Publish to npm - env: - VERSION: ${{ needs.release.outputs.version }} - NPM_TAG: ${{ needs.release.outputs.npm_tag }} - run: | - set -euo pipefail - prefix="" - if [[ -n "${NPM_TAG}" ]]; then - prefix="${NPM_TAG}-" - fi - - root_tarball="dist/npm/codex-npm-${VERSION}.tgz" - sdk_tarball="dist/npm/codex-sdk-npm-${VERSION}.tgz" - # Keep this list in sync with CODEX_PLATFORM_PACKAGES in - # codex-cli/scripts/build_npm_package.py. The root wrapper advances - # @openai/codex@latest as soon as it publishes, so every platform - # package it aliases must already exist in the registry first. - platform_tarballs=( - "dist/npm/codex-npm-linux-x64-${VERSION}.tgz" - "dist/npm/codex-npm-linux-arm64-${VERSION}.tgz" - "dist/npm/codex-npm-darwin-x64-${VERSION}.tgz" - "dist/npm/codex-npm-darwin-arm64-${VERSION}.tgz" - "dist/npm/codex-npm-win32-x64-${VERSION}.tgz" - "dist/npm/codex-npm-win32-arm64-${VERSION}.tgz" - ) - - for required_tarball in "${platform_tarballs[@]}" "${root_tarball}"; do - if [[ ! -f "${required_tarball}" ]]; then - echo "Missing npm tarball: ${required_tarball}" - exit 1 - fi - done - - shopt -s nullglob - other_tarballs=() - for tarball in dist/npm/*-"${VERSION}".tgz; do - if [[ "${tarball}" == "${root_tarball}" || "${tarball}" == "${sdk_tarball}" ]]; then - continue - fi - - is_platform_tarball=false - for platform_tarball in "${platform_tarballs[@]}"; do - if [[ "${tarball}" == "${platform_tarball}" ]]; then - is_platform_tarball=true - break - fi - done - if [[ "${is_platform_tarball}" == true ]]; then - continue - fi - - other_tarballs+=("${tarball}") - done - - # Publish the platform packages before the root CLI wrapper. The root - # wrapper advances @openai/codex@latest, so it should only publish - # after the optional dependency versions it references exist. - tarballs=( - "${platform_tarballs[@]}" - "${other_tarballs[@]}" - "${root_tarball}" - ) - if [[ -f "${sdk_tarball}" ]]; then - tarballs+=("${sdk_tarball}") - fi - - for tarball in "${tarballs[@]}"; do - filename="$(basename "${tarball}")" - tag="" - - case "${filename}" in - codex-npm-linux-*-"${VERSION}".tgz|codex-npm-darwin-*-"${VERSION}".tgz|codex-npm-win32-*-"${VERSION}".tgz) - platform="${filename#codex-npm-}" - platform="${platform%-${VERSION}.tgz}" - tag="${prefix}${platform}" - ;; - codex-npm-"${VERSION}".tgz|codex-responses-api-proxy-npm-"${VERSION}".tgz|codex-sdk-npm-"${VERSION}".tgz) - tag="${NPM_TAG}" - ;; - *) - echo "Unexpected npm tarball: ${filename}" - exit 1 - ;; - esac - - publish_cmd=(npm publish "${GITHUB_WORKSPACE}/${tarball}") - if [[ -n "${tag}" ]]; then - publish_cmd+=(--tag "${tag}") - fi - - echo "+ ${publish_cmd[*]}" - set +e - publish_output="$("${publish_cmd[@]}" 2>&1)" - publish_status=$? - set -e - - echo "${publish_output}" - if [[ ${publish_status} -eq 0 ]]; then - continue - fi - - if grep -qiE "previously published|cannot publish over|version already exists" <<< "${publish_output}"; then - echo "Skipping already-published package version for ${filename}" - continue - fi - - exit "${publish_status}" - done - - # Publish the platform-specific Python runtime wheels using PyPI trusted publishing. - # PyPI project configuration must trust this workflow and job. Keep this - # non-blocking while the Python runtime publishing path is new; failures still - # need release follow-up, but should not invalidate the Rust release itself. - publish-python-runtime: - # Publish to PyPI for stable releases and alpha pre-releases with numeric suffixes. - if: >- - ${{ - !cancelled() && - needs.release.result == 'success' && - needs.release.outputs.should_publish_python_runtime == 'true' - }} - name: publish-python-runtime - needs: release - runs-on: ubuntu-latest - continue-on-error: true - environment: pypi - permissions: - id-token: write # Required for PyPI trusted publishing. - contents: read - - steps: - - name: Download Python runtime wheels from release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - RELEASE_TAG: ${{ needs.release.outputs.tag }} - RELEASE_VERSION: ${{ needs.release.outputs.version }} - run: | - set -euo pipefail - python_version="$RELEASE_VERSION" - python_version="${python_version/-alpha./a}" - python_version="${python_version/-beta./b}" - python_version="${python_version/-rc./rc}" - - mkdir -p dist/python-runtime - gh release download "$RELEASE_TAG" \ - --repo "${GITHUB_REPOSITORY}" \ - --pattern "openai_codex_cli_bin-${python_version}-*.whl" \ - --dir dist/python-runtime - ls -lh dist/python-runtime - - - name: Publish Python runtime wheels to PyPI - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 - with: - packages-dir: dist/python-runtime - skip-existing: true - - winget: - name: winget - needs: release - # Only publish stable/mainline releases to WinGet; pre-releases include a - # '-' in the semver string (e.g., 1.2.3-alpha.1). - if: >- - ${{ - !cancelled() && - needs.release.result == 'success' && - needs.release.outputs.sign_macos == 'true' && - !contains(needs.release.outputs.version, '-') - }} - # This job only invokes a GitHub Action to open/update the winget-pkgs PR; - # it does not execute Windows-only tooling, so Linux is sufficient. - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: Publish to WinGet - uses: vedantmgoyal9/winget-releaser@7bd472be23763def6e16bd06cc8b1cdfab0e2fd5 - with: - identifier: OpenAI.Codex - version: ${{ needs.release.outputs.version }} - release-tag: ${{ needs.release.outputs.tag }} - fork-user: openai-oss-forks - installers-regex: '^codex-(?:x86_64|aarch64)-pc-windows-msvc\.exe\.zip$' - token: ${{ secrets.WINGET_PUBLISH_PAT }} - - update-branch: - name: Update latest-alpha-cli branch - if: >- - ${{ - !cancelled() && - needs.release.result == 'success' && - needs.release.outputs.sign_macos == 'true' - }} - permissions: - contents: write - needs: release - runs-on: ubuntu-latest - - steps: - - name: Update latest-alpha-cli branch - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -euo pipefail - gh api \ - repos/${GITHUB_REPOSITORY}/git/refs/heads/latest-alpha-cli \ - -X PATCH \ - -f sha="${GITHUB_SHA}" \ - -F force=true diff --git a/reference/openai-codex/.github/workflows/rusty-v8-release.yml b/reference/openai-codex/.github/workflows/rusty-v8-release.yml deleted file mode 100644 index 3b56a1e..0000000 --- a/reference/openai-codex/.github/workflows/rusty-v8-release.yml +++ /dev/null @@ -1,313 +0,0 @@ -name: rusty-v8-release - -on: - push: - tags: - - "rusty-v8-v*.*.*" - -concurrency: - group: ${{ github.workflow }}::${{ github.ref_name }} - cancel-in-progress: false - -jobs: - metadata: - runs-on: ubuntu-latest - outputs: - release_tag: ${{ steps.release_tag.outputs.release_tag }} - v8_version: ${{ steps.v8_version.outputs.version }} - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "3.12" - - - name: Resolve exact v8 crate version - id: v8_version - shell: bash - run: | - set -euo pipefail - version="$(python3 .github/scripts/rusty_v8_bazel.py resolved-v8-crate-version)" - echo "version=${version}" >> "$GITHUB_OUTPUT" - - - name: Resolve release tag - id: release_tag - env: - GITHUB_REF_NAME: ${{ github.ref_name }} - V8_VERSION: ${{ steps.v8_version.outputs.version }} - shell: bash - run: | - set -euo pipefail - - expected_release_tag="rusty-v8-v${V8_VERSION}" - release_tag="${GITHUB_REF_NAME}" - if [[ "${release_tag}" != "${expected_release_tag}" ]]; then - echo "Tag ${release_tag} does not match expected release tag ${expected_release_tag}." >&2 - exit 1 - fi - - echo "release_tag=${release_tag}" >> "$GITHUB_OUTPUT" - - build: - name: Build ${{ matrix.variant }} ${{ matrix.target }} - needs: metadata - runs-on: ${{ matrix.runner }} - permissions: - contents: read - actions: read - strategy: - fail-fast: false - matrix: - include: - - runner: ubuntu-24.04 - bazel_config: ci-v8 - platform: linux_amd64 - sandbox: false - target: x86_64-unknown-linux-gnu - variant: release - - runner: ubuntu-24.04 - bazel_config: ci-v8 - platform: linux_amd64 - sandbox: true - target: x86_64-unknown-linux-gnu - variant: ptrcomp-sandbox - - runner: ubuntu-24.04-arm - bazel_config: ci-v8 - platform: linux_arm64 - sandbox: false - target: aarch64-unknown-linux-gnu - variant: release - - runner: ubuntu-24.04-arm - bazel_config: ci-v8 - platform: linux_arm64 - sandbox: true - target: aarch64-unknown-linux-gnu - variant: ptrcomp-sandbox - - runner: macos-15-xlarge - bazel_config: ci-macos - platform: macos_amd64 - sandbox: false - target: x86_64-apple-darwin - variant: release - - runner: macos-15-xlarge - bazel_config: ci-macos - platform: macos_amd64 - sandbox: true - target: x86_64-apple-darwin - variant: ptrcomp-sandbox - - runner: macos-15-xlarge - bazel_config: ci-macos - platform: macos_arm64 - sandbox: false - target: aarch64-apple-darwin - variant: release - - runner: macos-15-xlarge - bazel_config: ci-macos - platform: macos_arm64 - sandbox: true - target: aarch64-apple-darwin - variant: ptrcomp-sandbox - - runner: ubuntu-24.04 - bazel_config: ci-v8 - platform: linux_amd64_musl - sandbox: false - target: x86_64-unknown-linux-musl - variant: release - - runner: ubuntu-24.04-arm - bazel_config: ci-v8 - platform: linux_arm64_musl - sandbox: false - target: aarch64-unknown-linux-musl - variant: release - - runner: ubuntu-24.04 - bazel_config: ci-v8 - platform: linux_amd64_musl - sandbox: true - target: x86_64-unknown-linux-musl - variant: ptrcomp-sandbox - - runner: ubuntu-24.04-arm - bazel_config: ci-v8 - platform: linux_arm64_musl - sandbox: true - target: aarch64-unknown-linux-musl - variant: ptrcomp-sandbox - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - - name: Set up Bazel - uses: ./.github/actions/setup-bazel-ci - with: - target: ${{ matrix.target }} - - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "3.12" - - - name: Set up Rust toolchain for Cargo smoke - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - toolchain: "1.93.0" - - - name: Build Bazel V8 release pair - env: - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - PLATFORM: ${{ matrix.platform }} - SANDBOX: ${{ matrix.sandbox }} - TARGET: ${{ matrix.target }} - shell: bash - run: | - set -euo pipefail - - target_suffix="${TARGET//-/_}" - pair_kind="release_pair" - if [[ "${SANDBOX}" == "true" ]]; then - pair_kind="sandbox_release_pair" - fi - pair_target="//third_party/v8:rusty_v8_${pair_kind}_${target_suffix}" - - bazel_args=( - build - -c - opt - "--platforms=@llvm//platforms:${PLATFORM}" - --config=rusty-v8-upstream-libcxx - "${pair_target}" - --build_metadata=COMMIT_SHA=$(git rev-parse HEAD) - ) - if [[ "${SANDBOX}" != "true" ]]; then - bazel_args+=(--config=v8-release-compat) - fi - - bazel \ - --noexperimental_remote_repo_contents_cache \ - "${bazel_args[@]}" \ - "--config=${{ matrix.bazel_config }}" \ - "--remote_header=x-buildbuddy-api-key=${BUILDBUDDY_API_KEY}" - - - name: Stage release pair - env: - BAZEL_CONFIG: ${{ matrix.bazel_config }} - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - PLATFORM: ${{ matrix.platform }} - SANDBOX: ${{ matrix.sandbox }} - TARGET: ${{ matrix.target }} - shell: bash - run: | - set -euo pipefail - - stage_args=( - --platform "${PLATFORM}" - --target "${TARGET}" - --compilation-mode opt - --output-dir "dist/${TARGET}" - --bazel-config "${BAZEL_CONFIG}" - ) - if [[ "${SANDBOX}" == "true" ]]; then - stage_args+=(--sandbox) - else - stage_args+=(--bazel-config v8-release-compat) - fi - - python3 .github/scripts/rusty_v8_bazel.py stage-release-pair "${stage_args[@]}" - - - name: Smoke test staged artifact with Cargo - env: - SANDBOX: ${{ matrix.sandbox }} - TARGET: ${{ matrix.target }} - shell: bash - run: | - set -euo pipefail - - host_arch="$(uname -m)" - case "${TARGET}:${host_arch}" in - x86_64-apple-darwin:x86_64|aarch64-apple-darwin:arm64|x86_64-unknown-linux-gnu:x86_64|aarch64-unknown-linux-gnu:aarch64) - ;; - *) - echo "Skipping non-native Cargo smoke for ${TARGET} on ${host_arch}." - exit 0 - ;; - esac - - archive="$(find "dist/${TARGET}" -maxdepth 1 -type f -name 'librusty_v8_*.a.gz' -print -quit)" - binding="$(find "dist/${TARGET}" -maxdepth 1 -type f -name 'src_binding_*.rs' -print -quit)" - if [[ -z "${archive}" || -z "${binding}" ]]; then - echo "Missing staged archive or binding for ${TARGET}." >&2 - exit 1 - fi - - cargo_args=(test -p codex-v8-poc) - if [[ "${SANDBOX}" == "true" ]]; then - cargo_args+=(--features sandbox) - fi - - ( - cd codex-rs - CARGO_TARGET_DIR="${RUNNER_TEMP}/rusty-v8-cargo-smoke-${TARGET}-${SANDBOX}" \ - RUSTY_V8_ARCHIVE="${GITHUB_WORKSPACE}/${archive}" \ - RUSTY_V8_SRC_BINDING_PATH="${GITHUB_WORKSPACE}/${binding}" \ - cargo "${cargo_args[@]}" - ) - - - name: Upload staged artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: rusty-v8-${{ needs.metadata.outputs.v8_version }}-${{ matrix.variant }}-${{ matrix.target }} - path: dist/${{ matrix.target }}/* - - publish-release: - needs: - - metadata - - build - runs-on: ubuntu-latest - permissions: - contents: write - actions: read - - steps: - - name: Check whether release already exists - id: release - env: - GH_TOKEN: ${{ github.token }} - RELEASE_TAG: ${{ needs.metadata.outputs.release_tag }} - shell: bash - run: | - set -euo pipefail - - if gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" > /dev/null 2>&1; then - echo "exists=true" >> "${GITHUB_OUTPUT}" - else - echo "exists=false" >> "${GITHUB_OUTPUT}" - fi - - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - path: dist - - - name: Create GitHub Release - if: ${{ steps.release.outputs.exists != 'true' }} - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 - with: - tag_name: ${{ needs.metadata.outputs.release_tag }} - name: ${{ needs.metadata.outputs.release_tag }} - files: dist/** - # Keep V8 artifact releases out of Codex's normal "latest release" channel. - prerelease: true - - - name: Amend existing GitHub Release - if: ${{ steps.release.outputs.exists == 'true' }} - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 - with: - tag_name: ${{ needs.metadata.outputs.release_tag }} - name: ${{ needs.metadata.outputs.release_tag }} - files: dist/** - overwrite_files: true - # Keep V8 artifact releases out of Codex's normal "latest release" channel. - prerelease: true diff --git a/reference/openai-codex/.github/workflows/sdk.yml b/reference/openai-codex/.github/workflows/sdk.yml deleted file mode 100644 index 54ed8dc..0000000 --- a/reference/openai-codex/.github/workflows/sdk.yml +++ /dev/null @@ -1,160 +0,0 @@ -name: sdk - -on: - push: - branches: [main] - pull_request: {} - -jobs: - python-sdk: - runs-on: - group: codex-runners - labels: codex-linux-x64 - timeout-minutes: 10 - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Test Python SDK - shell: bash - run: | - set -euo pipefail - - # Run inside Alpine so dependency resolution exercises the pinned - # runtime wheel on the same Linux wheel family that CI installs. - docker run --rm \ - --user "$(id -u):$(id -g)" \ - -e HOME=/tmp/codex-python-sdk-home \ - -e UV_LINK_MODE=copy \ - -v "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}" \ - -w "${GITHUB_WORKSPACE}/sdk/python" \ - python:3.12-alpine \ - sh -euxc ' - python -m venv /tmp/uv - /tmp/uv/bin/python -m pip install uv==0.11.3 - /tmp/uv/bin/uv sync --extra dev --frozen - /tmp/uv/bin/uv run --extra dev ruff check --output-format=github . - /tmp/uv/bin/uv run --extra dev ruff format --check . - /tmp/uv/bin/uv run --extra dev pytest - ' - - sdks: - runs-on: - group: codex-runners - labels: codex-linux-x64 - timeout-minutes: 10 - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Install Linux bwrap build dependencies - shell: bash - run: | - set -euo pipefail - sudo apt-get update -y - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev - - - name: Setup pnpm - uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5 - with: - run_install: false - - - name: Setup Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 - with: - node-version: 22 - cache: pnpm - - - name: Set up Bazel CI - id: setup_bazel - uses: ./.github/actions/setup-bazel-ci - with: - target: x86_64-unknown-linux-gnu - - - name: Build codex with Bazel - env: - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - shell: bash - run: | - set -euo pipefail - # Use the shared CI wrapper so fork PRs fall back cleanly when - # BuildBuddy credentials are unavailable. This workflow needs the - # built `codex` binary on disk afterwards, so ask the wrapper to - # override CI's default remote_download_minimal behavior. - ./.github/scripts/run-bazel-ci.sh \ - --remote-download-toplevel \ - -- \ - build \ - --build_metadata=COMMIT_SHA=${GITHUB_SHA} \ - --build_metadata=TAG_job=sdk \ - -- \ - //codex-rs/cli:codex - - # Resolve the exact output file using the same wrapper/config path as - # the build instead of guessing which Bazel convenience symlink is - # available on the runner. - cquery_output="$( - ./.github/scripts/run-bazel-ci.sh \ - -- \ - cquery \ - --output=files \ - -- \ - //codex-rs/cli:codex \ - | grep -E '^(/|bazel-out/)' \ - | tail -n 1 - )" - if [[ "${cquery_output}" = /* ]]; then - codex_bazel_output_path="${cquery_output}" - else - codex_bazel_output_path="${GITHUB_WORKSPACE}/${cquery_output}" - fi - if [[ -z "${codex_bazel_output_path}" ]]; then - echo "Bazel did not report an output path for //codex-rs/cli:codex." >&2 - exit 1 - fi - if [[ ! -e "${codex_bazel_output_path}" ]]; then - echo "Unable to locate the Bazel-built codex binary at ${codex_bazel_output_path}." >&2 - exit 1 - fi - - # Stage the binary into the workspace and point the SDK tests at that - # stable path. The tests spawn `codex` directly many times, so using a - # normal executable path is more reliable than invoking Bazel for each - # test process. - install_dir="${GITHUB_WORKSPACE}/.tmp/sdk-ci" - mkdir -p "${install_dir}" - install -m 755 "${codex_bazel_output_path}" "${install_dir}/codex" - echo "CODEX_EXEC_PATH=${install_dir}/codex" >> "$GITHUB_ENV" - - - name: Warm up Bazel-built codex - shell: bash - run: | - set -euo pipefail - "${CODEX_EXEC_PATH}" --version - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build SDK packages - run: pnpm -r --filter ./sdk/typescript run build - - - name: Lint SDK packages - run: pnpm -r --filter ./sdk/typescript run lint - - - name: Test SDK packages - run: pnpm -r --filter ./sdk/typescript run test - - - name: Save bazel repository cache - if: always() && !cancelled() && steps.setup_bazel.outputs.cache-hit != 'true' - continue-on-error: true - uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - with: - path: | - ~/.cache/bazel-repo-cache - key: bazel-cache-x86_64-unknown-linux-gnu-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }} diff --git a/reference/openai-codex/.github/workflows/v8-canary.yml b/reference/openai-codex/.github/workflows/v8-canary.yml deleted file mode 100644 index 979e991..0000000 --- a/reference/openai-codex/.github/workflows/v8-canary.yml +++ /dev/null @@ -1,411 +0,0 @@ -name: v8-canary - -on: - pull_request: - paths: - - ".bazelrc" - - ".github/actions/setup-bazel-ci/**" - - ".github/scripts/rusty_v8_bazel.py" - - ".github/scripts/rusty_v8_module_bazel.py" - - ".github/workflows/rusty-v8-release.yml" - - ".github/workflows/v8-canary.yml" - - "MODULE.bazel" - - "MODULE.bazel.lock" - - "codex-rs/Cargo.toml" - - "patches/BUILD.bazel" - - "patches/llvm_*.patch" - - "patches/rules_cc_*.patch" - - "patches/v8_*.patch" - - "third_party/v8/**" - push: - branches: - - main - paths: - - ".bazelrc" - - ".github/actions/setup-bazel-ci/**" - - ".github/scripts/rusty_v8_bazel.py" - - ".github/scripts/rusty_v8_module_bazel.py" - - ".github/workflows/rusty-v8-release.yml" - - ".github/workflows/v8-canary.yml" - - "MODULE.bazel" - - "MODULE.bazel.lock" - - "codex-rs/Cargo.toml" - - "patches/BUILD.bazel" - - "patches/llvm_*.patch" - - "patches/rules_cc_*.patch" - - "patches/v8_*.patch" - - "third_party/v8/**" - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}::${{ github.event.pull_request.number > 0 && format('pr-{0}', github.event.pull_request.number) || github.ref_name }} - cancel-in-progress: ${{ github.ref_name != 'main' }} - -jobs: - metadata: - runs-on: ubuntu-latest - outputs: - v8_version: ${{ steps.v8_version.outputs.version }} - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "3.12" - - - name: Resolve exact v8 crate version - id: v8_version - shell: bash - run: | - set -euo pipefail - version="$(python3 .github/scripts/rusty_v8_bazel.py resolved-v8-crate-version)" - echo "version=${version}" >> "$GITHUB_OUTPUT" - - build: - name: Build ${{ matrix.variant }} ${{ matrix.target }} - needs: metadata - runs-on: ${{ matrix.runner }} - permissions: - contents: read - actions: read - strategy: - fail-fast: false - matrix: - include: - - runner: ubuntu-24.04 - bazel_config: ci-v8 - platform: linux_amd64 - sandbox: false - target: x86_64-unknown-linux-gnu - variant: release - - runner: ubuntu-24.04 - bazel_config: ci-v8 - platform: linux_amd64 - sandbox: true - target: x86_64-unknown-linux-gnu - variant: ptrcomp-sandbox - - runner: ubuntu-24.04-arm - bazel_config: ci-v8 - platform: linux_arm64 - sandbox: false - target: aarch64-unknown-linux-gnu - variant: release - - runner: ubuntu-24.04-arm - bazel_config: ci-v8 - platform: linux_arm64 - sandbox: true - target: aarch64-unknown-linux-gnu - variant: ptrcomp-sandbox - - runner: macos-15-xlarge - bazel_config: ci-macos - platform: macos_amd64 - sandbox: false - target: x86_64-apple-darwin - variant: release - - runner: macos-15-xlarge - bazel_config: ci-macos - platform: macos_amd64 - sandbox: true - target: x86_64-apple-darwin - variant: ptrcomp-sandbox - - runner: macos-15-xlarge - bazel_config: ci-macos - platform: macos_arm64 - sandbox: false - target: aarch64-apple-darwin - variant: release - - runner: macos-15-xlarge - bazel_config: ci-macos - platform: macos_arm64 - sandbox: true - target: aarch64-apple-darwin - variant: ptrcomp-sandbox - - runner: ubuntu-24.04 - bazel_config: ci-v8 - platform: linux_amd64_musl - sandbox: false - target: x86_64-unknown-linux-musl - variant: release - - runner: ubuntu-24.04 - bazel_config: ci-v8 - platform: linux_amd64_musl - sandbox: true - target: x86_64-unknown-linux-musl - variant: ptrcomp-sandbox - - runner: ubuntu-24.04-arm - bazel_config: ci-v8 - platform: linux_arm64_musl - sandbox: false - target: aarch64-unknown-linux-musl - variant: release - - runner: ubuntu-24.04-arm - bazel_config: ci-v8 - platform: linux_arm64_musl - sandbox: true - target: aarch64-unknown-linux-musl - variant: ptrcomp-sandbox - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - persist-credentials: false - - - name: Set up Bazel - uses: ./.github/actions/setup-bazel-ci - with: - target: ${{ matrix.target }} - - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "3.12" - - - name: Set up Rust toolchain for Cargo smoke - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - toolchain: "1.93.0" - - - name: Build Bazel V8 release pair - env: - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - PLATFORM: ${{ matrix.platform }} - SANDBOX: ${{ matrix.sandbox }} - TARGET: ${{ matrix.target }} - shell: bash - run: | - set -euo pipefail - - target_suffix="${TARGET//-/_}" - pair_kind="release_pair" - if [[ "${SANDBOX}" == "true" ]]; then - pair_kind="sandbox_release_pair" - fi - pair_target="//third_party/v8:rusty_v8_${pair_kind}_${target_suffix}" - - bazel_args=( - build - "--platforms=@llvm//platforms:${PLATFORM}" - --config=rusty-v8-upstream-libcxx - "${pair_target}" - --build_metadata=COMMIT_SHA=$(git rev-parse HEAD) - ) - if [[ "${SANDBOX}" != "true" ]]; then - bazel_args+=(--config=v8-release-compat) - fi - - bazel \ - --noexperimental_remote_repo_contents_cache \ - "${bazel_args[@]}" \ - "--config=${{ matrix.bazel_config }}" \ - "--remote_header=x-buildbuddy-api-key=${BUILDBUDDY_API_KEY}" - - - name: Stage release pair - env: - BAZEL_CONFIG: ${{ matrix.bazel_config }} - BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }} - PLATFORM: ${{ matrix.platform }} - SANDBOX: ${{ matrix.sandbox }} - TARGET: ${{ matrix.target }} - shell: bash - run: | - set -euo pipefail - - stage_args=( - --platform "${PLATFORM}" - --target "${TARGET}" - --output-dir "dist/${TARGET}" - --bazel-config "${BAZEL_CONFIG}" - ) - if [[ "${SANDBOX}" == "true" ]]; then - stage_args+=(--sandbox) - else - stage_args+=(--bazel-config v8-release-compat) - fi - - python3 .github/scripts/rusty_v8_bazel.py stage-release-pair "${stage_args[@]}" - - - name: Smoke test staged artifact with Cargo - env: - SANDBOX: ${{ matrix.sandbox }} - TARGET: ${{ matrix.target }} - shell: bash - run: | - set -euo pipefail - - host_arch="$(uname -m)" - case "${TARGET}:${host_arch}" in - x86_64-apple-darwin:x86_64|aarch64-apple-darwin:arm64|x86_64-unknown-linux-gnu:x86_64|aarch64-unknown-linux-gnu:aarch64) - ;; - *) - echo "Skipping non-native Cargo smoke for ${TARGET} on ${host_arch}." - exit 0 - ;; - esac - - archive="$(find "dist/${TARGET}" -maxdepth 1 -type f -name 'librusty_v8_*.a.gz' -print -quit)" - binding="$(find "dist/${TARGET}" -maxdepth 1 -type f -name 'src_binding_*.rs' -print -quit)" - if [[ -z "${archive}" || -z "${binding}" ]]; then - echo "Missing staged archive or binding for ${TARGET}." >&2 - exit 1 - fi - - cargo_args=(test -p codex-v8-poc) - if [[ "${SANDBOX}" == "true" ]]; then - cargo_args+=(--features sandbox) - fi - - ( - cd codex-rs - CARGO_TARGET_DIR="${RUNNER_TEMP}/rusty-v8-cargo-smoke-${TARGET}-${SANDBOX}" \ - RUSTY_V8_ARCHIVE="${GITHUB_WORKSPACE}/${archive}" \ - RUSTY_V8_SRC_BINDING_PATH="${GITHUB_WORKSPACE}/${binding}" \ - cargo "${cargo_args[@]}" - ) - - - name: Upload staged artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: v8-canary-${{ needs.metadata.outputs.v8_version }}-${{ matrix.variant }}-${{ matrix.target }} - path: dist/${{ matrix.target }}/* - - build-windows-source: - name: Build ptrcomp-sandbox ${{ matrix.target }} from source - needs: metadata - runs-on: ${{ matrix.runner }} - permissions: - contents: read - strategy: - fail-fast: false - matrix: - include: - - runner: windows-2022 - target: x86_64-pc-windows-msvc - - runner: windows-2022 - target: aarch64-pc-windows-msvc - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Configure git for upstream checkout - shell: bash - run: git config --global core.symlinks true - - - name: Check out upstream rusty_v8 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - repository: denoland/rusty_v8 - ref: v${{ needs.metadata.outputs.v8_version }} - path: upstream-rusty-v8 - submodules: recursive - - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 - with: - python-version: "3.11" - architecture: x64 - - - name: Set up Codex Rust toolchain for Cargo smoke - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 - with: - toolchain: "1.93.0" - targets: ${{ matrix.target }} - - - name: Install rusty_v8 Rust toolchain - env: - TARGET: ${{ matrix.target }} - shell: bash - run: | - set -euo pipefail - rustup toolchain install 1.91.0 --profile minimal --no-self-update - rustup target add --toolchain 1.91.0 "${TARGET}" - - - name: Write upstream submodule status - shell: bash - working-directory: upstream-rusty-v8 - run: git submodule status --recursive > git_submodule_status.txt - - - name: Restore upstream source-build cache - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 - with: - path: | - upstream-rusty-v8/target/sccache - upstream-rusty-v8/target/${{ matrix.target }}/release/gn_out - key: rusty-v8-source-${{ matrix.target }}-sandbox-${{ hashFiles('upstream-rusty-v8/Cargo.lock', 'upstream-rusty-v8/build.rs', 'upstream-rusty-v8/git_submodule_status.txt') }} - restore-keys: | - rusty-v8-source-${{ matrix.target }}-sandbox- - - - name: Install and start sccache - shell: pwsh - env: - SCCACHE_CACHE_SIZE: 256M - SCCACHE_DIR: ${{ github.workspace }}/upstream-rusty-v8/target/sccache - SCCACHE_IDLE_TIMEOUT: 0 - run: | - $version = "v0.8.2" - $platform = "x86_64-pc-windows-msvc" - $basename = "sccache-$version-$platform" - $url = "https://github.com/mozilla/sccache/releases/download/$version/$basename.tar.gz" - cd ~ - curl -LO $url - tar -xzvf "$basename.tar.gz" - . $basename/sccache --start-server - echo "$(pwd)/$basename" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - - name: Install Chromium clang for ARM64 MSVC cross build - if: matrix.target == 'aarch64-pc-windows-msvc' - shell: bash - working-directory: upstream-rusty-v8 - run: python3 tools/clang/scripts/update.py - - - name: Build upstream rusty_v8 sandbox release pair - env: - SCCACHE_IDLE_TIMEOUT: 0 - TARGET: ${{ matrix.target }} - V8_FROM_SOURCE: "1" - shell: bash - working-directory: upstream-rusty-v8 - run: cargo +1.91.0 build --locked --release --target "${TARGET}" --features v8_enable_sandbox - - - name: Stage upstream sandbox release pair - env: - TARGET: ${{ matrix.target }} - shell: bash - run: | - set -euo pipefail - python3 .github/scripts/rusty_v8_bazel.py stage-upstream-release-pair \ - --source-root upstream-rusty-v8 \ - --target "${TARGET}" \ - --output-dir "dist/${TARGET}" \ - --sandbox - - - name: Smoke link staged artifact with Cargo - env: - TARGET: ${{ matrix.target }} - shell: bash - run: | - set -euo pipefail - - archive="$(find "dist/${TARGET}" -maxdepth 1 -type f -name 'rusty_v8_*.lib.gz' -print -quit)" - binding="$(find "dist/${TARGET}" -maxdepth 1 -type f -name 'src_binding_*.rs' -print -quit)" - if [[ -z "${archive}" || -z "${binding}" ]]; then - echo "Missing staged archive or binding for ${TARGET}." >&2 - exit 1 - fi - - ( - cd codex-rs - RUSTY_V8_ARCHIVE="${GITHUB_WORKSPACE}/${archive}" \ - RUSTY_V8_SRC_BINDING_PATH="${GITHUB_WORKSPACE}/${binding}" \ - cargo +1.93.0 test -p codex-v8-poc --target "${TARGET}" --features sandbox --no-run - ) - - - name: Upload staged artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: v8-canary-${{ needs.metadata.outputs.v8_version }}-ptrcomp-sandbox-${{ matrix.target }} - path: dist/${{ matrix.target }}/* diff --git a/reference/openai-codex/.github/workflows/zstd b/reference/openai-codex/.github/workflows/zstd deleted file mode 100755 index 7c601a5..0000000 --- a/reference/openai-codex/.github/workflows/zstd +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env dotslash - -// This DotSlash file wraps zstd for Windows runners. -// The upstream release provides win32/win64 binaries; for windows-aarch64 we -// use the win64 artifact via Windows x64 emulation. -{ - "name": "zstd", - "platforms": { - "windows-x86_64": { - "size": 1747181, - "hash": "sha256", - "digest": "acb4e8111511749dc7a3ebedca9b04190e37a17afeb73f55d4425dbf0b90fad9", - "format": "zip", - "path": "zstd-v1.5.7-win64/zstd.exe", - "providers": [ - { - "url": "https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-v1.5.7-win64.zip" - }, - { - "type": "github-release", - "repo": "facebook/zstd", - "tag": "v1.5.7", - "name": "zstd-v1.5.7-win64.zip" - } - ] - }, - "windows-aarch64": { - "size": 1747181, - "hash": "sha256", - "digest": "acb4e8111511749dc7a3ebedca9b04190e37a17afeb73f55d4425dbf0b90fad9", - "format": "zip", - "path": "zstd-v1.5.7-win64/zstd.exe", - "providers": [ - { - "url": "https://github.com/facebook/zstd/releases/download/v1.5.7/zstd-v1.5.7-win64.zip" - }, - { - "type": "github-release", - "repo": "facebook/zstd", - "tag": "v1.5.7", - "name": "zstd-v1.5.7-win64.zip" - } - ] - } - } -} diff --git a/reference/openai-codex/.gitignore b/reference/openai-codex/.gitignore deleted file mode 100644 index f792773..0000000 --- a/reference/openai-codex/.gitignore +++ /dev/null @@ -1,94 +0,0 @@ -# deps -# Node.js dependencies -node_modules -.pnpm-store -.pnpm-debug.log - -# Keep pnpm-lock.yaml -!pnpm-lock.yaml - -# build -dist/ -bazel-* -user.bazelrc -build/ -out/ -storybook-static/ - -# ignore README for publishing -codex-cli/README.md - -# ignore Nix derivation results -result - -# editor -.vscode/ -.idea/ -.history/ -.zed/ -*.swp -*~ - -# cli tools -CLAUDE.md -.claude/ -AGENTS.override.md - -# caches -.cache/ -.turbo/ -.parcel-cache/ -.eslintcache -.nyc_output/ -.jest/ -*.tsbuildinfo - -# logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# env -.env* -!.env.example -.venv/ - -# package -*.tgz - -# ci -.vercel/ -.netlify/ - -# patches -apply_patch/ - -# coverage -coverage/ - -# personal files -personal/ - -# os -.DS_Store -Thumbs.db -Icon? -.Spotlight-V100/ - -# Unwanted package managers -.yarn/ -yarn.lock - -# release -package.json-e -session.ts-e -CHANGELOG.ignore.md - -# nix related -.direnv -.envrc - -# Python bytecode files -__pycache__/ -*.pyc diff --git a/reference/openai-codex/.gitkeep b/reference/openai-codex/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/reference/openai-codex/.markdownlint-cli2.yaml b/reference/openai-codex/.markdownlint-cli2.yaml deleted file mode 100644 index 15b472c..0000000 --- a/reference/openai-codex/.markdownlint-cli2.yaml +++ /dev/null @@ -1,6 +0,0 @@ -config: - MD013: - line_length: 100 - -globs: - - "docs/tui-chat-composer.md" diff --git a/reference/openai-codex/.npmrc b/reference/openai-codex/.npmrc deleted file mode 100644 index 4153fe8..0000000 --- a/reference/openai-codex/.npmrc +++ /dev/null @@ -1,4 +0,0 @@ -shamefully-hoist=true -strict-peer-dependencies=false -node-linker=hoisted -prefer-workspace-packages=true diff --git a/reference/openai-codex/.prettierignore b/reference/openai-codex/.prettierignore deleted file mode 100644 index f5b50f6..0000000 --- a/reference/openai-codex/.prettierignore +++ /dev/null @@ -1,7 +0,0 @@ -/codex-cli/dist -/codex-cli/node_modules -pnpm-lock.yaml - -prompt.md -*_prompt.md -*_instructions.md diff --git a/reference/openai-codex/.prettierrc.toml b/reference/openai-codex/.prettierrc.toml deleted file mode 100644 index 4c58c58..0000000 --- a/reference/openai-codex/.prettierrc.toml +++ /dev/null @@ -1,8 +0,0 @@ -printWidth = 80 -quoteProps = "consistent" -semi = true -tabWidth = 2 -trailingComma = "all" - -# Preserve existing behavior for markdown/text wrapping. -proseWrap = "preserve" diff --git a/reference/opencode-1.15.5/.editorconfig b/reference/opencode-1.15.5/.editorconfig deleted file mode 100755 index aada95f..0000000 --- a/reference/opencode-1.15.5/.editorconfig +++ /dev/null @@ -1,9 +0,0 @@ -root = true - -[*] -charset = utf-8 -insert_final_newline = true -end_of_line = lf -indent_style = space -indent_size = 2 -max_line_length = 80 diff --git a/reference/opencode-1.15.5/.github/CODEOWNERS b/reference/opencode-1.15.5/.github/CODEOWNERS deleted file mode 100755 index 3aeef82..0000000 --- a/reference/opencode-1.15.5/.github/CODEOWNERS +++ /dev/null @@ -1,5 +0,0 @@ -# web + desktop packages -packages/app/ @adamdotdevin -packages/tauri/ @adamdotdevin -packages/desktop/src-tauri/ @brendonovich -packages/desktop/ @adamdotdevin diff --git a/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/bug-report.yml b/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/bug-report.yml deleted file mode 100755 index 96234eb..0000000 --- a/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/bug-report.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Bug report -description: Report an issue that should be fixed -body: - - type: textarea - id: description - attributes: - label: Description - description: Describe the bug you encountered - placeholder: What happened? - validations: - required: true - - - type: input - id: plugins - attributes: - label: Plugins - description: What plugins are you using? - validations: - required: false - - - type: input - id: opencode-version - attributes: - label: OpenCode version - description: What version of OpenCode are you using? - validations: - required: false - - - type: textarea - id: reproduce - attributes: - label: Steps to reproduce - description: How can we reproduce this issue? - placeholder: | - 1. - 2. - 3. - validations: - required: false - - - type: textarea - id: screenshot-or-link - attributes: - label: Screenshot and/or share link - description: Run `/share` to get a share link, or attach a screenshot - placeholder: Paste link or drag and drop screenshot here - validations: - required: false - - - type: input - id: os - attributes: - label: Operating System - description: what OS are you using? - placeholder: e.g., macOS 26.0.1, Ubuntu 22.04, Windows 11 - validations: - required: false - - - type: input - id: terminal - attributes: - label: Terminal - description: what terminal are you using? - placeholder: e.g., iTerm2, Ghostty, Alacritty, Windows Terminal - validations: - required: false diff --git a/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/config.yml b/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100755 index 52eec90..0000000 --- a/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: 💬 Discord Community - url: https://discord.gg/opencode - about: For quick questions or real-time discussion. Note that issues are searchable and help others with the same question. diff --git a/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/feature-request.yml b/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/feature-request.yml deleted file mode 100755 index 42f1d3c..0000000 --- a/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/feature-request.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: 🚀 Feature Request -description: Suggest an idea, feature, or enhancement -title: "[FEATURE]:" - -body: - - type: checkboxes - id: verified - attributes: - label: Feature hasn't been suggested before. - options: - - label: I have verified this feature I'm about to request hasn't been suggested before. - required: true - - - type: textarea - attributes: - label: Describe the enhancement you want to request - description: What do you want to change or add? What are the benefits of implementing this? Try to be detailed so we can understand your request better :) - validations: - required: true diff --git a/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/question.yml b/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/question.yml deleted file mode 100755 index 8930ba6..0000000 --- a/reference/opencode-1.15.5/.github/ISSUE_TEMPLATE/question.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Question -description: Ask a question -body: - - type: textarea - id: question - attributes: - label: Question - description: What's your question? - validations: - required: true diff --git a/reference/opencode-1.15.5/.github/TEAM_MEMBERS b/reference/opencode-1.15.5/.github/TEAM_MEMBERS deleted file mode 100755 index a662c7c..0000000 --- a/reference/opencode-1.15.5/.github/TEAM_MEMBERS +++ /dev/null @@ -1,16 +0,0 @@ -adamdotdevin -Brendonovich -fwang -Hona -iamdavidhill -jayair -jlongster -kitlangton -kommander -MrMushrooooom -nexxeln -R44VC0RP -rekram1-node -thdxr -simonklee -vimtor diff --git a/reference/opencode-1.15.5/.github/actions/setup-bun/action.yml b/reference/opencode-1.15.5/.github/actions/setup-bun/action.yml deleted file mode 100755 index 5b44517..0000000 --- a/reference/opencode-1.15.5/.github/actions/setup-bun/action.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: "Setup Bun" -description: "Setup Bun with caching and install dependencies" -inputs: - install-flags: - description: "Additional flags to pass to 'bun install'" - required: false - default: "" -runs: - using: "composite" - steps: - - name: Get baseline download URL - id: bun-url - shell: bash - run: | - if [ "$RUNNER_ARCH" = "X64" ]; then - V=$(node -p "require('./package.json').packageManager.split('@')[1]") - case "$RUNNER_OS" in - macOS) OS=darwin ;; - Linux) OS=linux ;; - Windows) OS=windows ;; - esac - echo "url=https://github.com/oven-sh/bun/releases/download/bun-v${V}/bun-${OS}-x64-baseline.zip" >> "$GITHUB_OUTPUT" - fi - - - name: Setup Bun - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - with: - bun-version-file: ${{ !steps.bun-url.outputs.url && 'package.json' || '' }} - bun-download-url: ${{ steps.bun-url.outputs.url }} - - - name: Get cache directory - id: cache - shell: bash - run: echo "dir=$(bun pm cache)" >> "$GITHUB_OUTPUT" - - - name: Restore Bun dependencies - id: bun-cache - uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: ${{ steps.cache.outputs.dir }} - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun- - - - name: Install setuptools for distutils compatibility - run: python3 -m pip install setuptools || pip install setuptools || true - shell: bash - - - name: Install dependencies - run: | - # Workaround for patched peer variants - # e.g. ./patches/ for standard-openapi - # https://github.com/oven-sh/bun/issues/28147 - if [ "$RUNNER_OS" = "Windows" ]; then - bun install --linker hoisted ${{ inputs.install-flags }} - else - bun install ${{ inputs.install-flags }} - fi - shell: bash - - - name: Save Bun dependencies - if: steps.bun-cache.outputs.cache-hit != 'true' && github.event_name != 'pull_request' && github.event_name != 'pull_request_target' - uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: ${{ steps.cache.outputs.dir }} - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} diff --git a/reference/opencode-1.15.5/.github/actions/setup-git-committer/action.yml b/reference/opencode-1.15.5/.github/actions/setup-git-committer/action.yml deleted file mode 100755 index 65c974c..0000000 --- a/reference/opencode-1.15.5/.github/actions/setup-git-committer/action.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: "Setup Git Committer" -description: "Create app token and configure git user" -inputs: - opencode-app-id: - description: "OpenCode GitHub App ID" - required: true - opencode-app-secret: - description: "OpenCode GitHub App private key" - required: true -outputs: - token: - description: "GitHub App token" - value: ${{ steps.apptoken.outputs.token }} - app-slug: - description: "GitHub App slug" - value: ${{ steps.apptoken.outputs.app-slug }} -runs: - using: "composite" - steps: - - name: Create app token - id: apptoken - uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2 - with: - app-id: ${{ inputs.opencode-app-id }} - private-key: ${{ inputs.opencode-app-secret }} - owner: ${{ github.repository_owner }} - - - name: Configure git user - run: | - slug="${{ steps.apptoken.outputs.app-slug }}" - git config --global user.name "${slug}[bot]" - git config --global user.email "${slug}[bot]@users.noreply.github.com" - shell: bash - - - name: Clear checkout auth - run: | - git config --local --unset-all http.https://github.com/.extraheader || true - shell: bash - - - name: Configure git remote - run: | - git remote set-url origin https://x-access-token:${{ steps.apptoken.outputs.token }}@github.com/${{ github.repository }} - shell: bash diff --git a/reference/opencode-1.15.5/.github/publish-python-sdk.yml b/reference/opencode-1.15.5/.github/publish-python-sdk.yml deleted file mode 100755 index 151ecb9..0000000 --- a/reference/opencode-1.15.5/.github/publish-python-sdk.yml +++ /dev/null @@ -1,71 +0,0 @@ -# -# This file is intentionally in the wrong dir, will move and add later.... -# - -# name: publish-python-sdk - -# on: -# release: -# types: [published] -# workflow_dispatch: - -# jobs: -# publish: -# runs-on: ubuntu-latest -# permissions: -# contents: read -# steps: -# - name: Checkout repository -# uses: actions/checkout@v4 - -# - name: Setup Bun -# uses: oven-sh/setup-bun@v1 -# with: -# bun-version: 1.2.21 - -# - name: Install dependencies (JS/Bun) -# run: bun install - -# - name: Install uv -# shell: bash -# run: curl -LsSf https://astral.sh/uv/install.sh | sh - -# - name: Generate Python SDK from OpenAPI (CLI) -# shell: bash -# run: | -# ~/.local/bin/uv run --project packages/sdk/python python packages/sdk/python/scripts/generate.py --source cli - -# - name: Sync Python dependencies -# shell: bash -# run: | -# ~/.local/bin/uv sync --dev --project packages/sdk/python - -# - name: Set version from release tag -# shell: bash -# run: | -# TAG="${GITHUB_REF_NAME:-}" -# if [ -z "$TAG" ]; then -# TAG="$(git describe --tags --abbrev=0 || echo 0.0.0)" -# fi -# echo "Using version: $TAG" -# VERSION="$TAG" ~/.local/bin/uv run --project packages/sdk/python python - <<'PY' -# import os, re, pathlib -# root = pathlib.Path('packages/sdk/python') -# pt = (root / 'pyproject.toml').read_text() -# version = os.environ.get('VERSION','0.0.0').lstrip('v') -# pt = re.sub(r'(?m)^(version\s*=\s*")[^"]+("\s*)$', f"\\1{version}\\2", pt) -# (root / 'pyproject.toml').write_text(pt) -# # Also update generator config override for consistency -# cfgp = root / 'openapi-python-client.yaml' -# if cfgp.exists(): -# cfg = cfgp.read_text() -# cfg = re.sub(r'(?m)^(package_version_override:\s*)\S+$', f"\\1{version}", cfg) -# cfgp.write_text(cfg) -# PY - -# - name: Build and publish to PyPI -# env: -# PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }} -# shell: bash -# run: | -# ~/.local/bin/uv run --project packages/sdk/python python packages/sdk/python/scripts/publish.py diff --git a/reference/opencode-1.15.5/.github/pull_request_template.md b/reference/opencode-1.15.5/.github/pull_request_template.md deleted file mode 100755 index 393bf90..0000000 --- a/reference/opencode-1.15.5/.github/pull_request_template.md +++ /dev/null @@ -1,29 +0,0 @@ -### Issue for this PR - -Closes # - -### Type of change - -- [ ] Bug fix -- [ ] New feature -- [ ] Refactor / code improvement -- [ ] Documentation - -### What does this PR do? - -Please provide a description of the issue, the changes you made to fix it, and why they work. It is expected that you understand why your changes work and if you do not understand why at least say as much so a maintainer knows how much to value the PR. - -**If you paste a large clearly AI generated description here your PR may be IGNORED or CLOSED!** - -### How did you verify your code works? - -### Screenshots / recordings - -_If this is a UI change, please include a screenshot or recording._ - -### Checklist - -- [ ] I have tested my changes locally -- [ ] I have not included unrelated changes in this PR - -_If you do not follow this template your PR will be automatically rejected._ diff --git a/reference/opencode-1.15.5/.github/workflows/beta.yml b/reference/opencode-1.15.5/.github/workflows/beta.yml deleted file mode 100755 index e93d5fb..0000000 --- a/reference/opencode-1.15.5/.github/workflows/beta.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: beta - -on: - workflow_dispatch: - schedule: - - cron: "0 * * * *" - -jobs: - sync: - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: write - pull-requests: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 0 - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Setup Git Committer - id: setup-git-committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Install OpenCode - run: bun i -g opencode-ai - - - name: Sync beta branch - env: - GH_TOKEN: ${{ steps.setup-git-committer.outputs.token }} - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - run: bun script/beta.ts diff --git a/reference/opencode-1.15.5/.github/workflows/close-issues.yml b/reference/opencode-1.15.5/.github/workflows/close-issues.yml deleted file mode 100755 index b8a2e3f..0000000 --- a/reference/opencode-1.15.5/.github/workflows/close-issues.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: close-issues - -on: - schedule: - - cron: "0 2 * * *" # Daily at 2:00 AM - workflow_dispatch: - -jobs: - close: - runs-on: ubuntu-latest - permissions: - contents: read - issues: write - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - with: - bun-version: latest - - - name: Close stale issues - env: - GITHUB_TOKEN: ${{ github.token }} - run: bun script/github/close-issues.ts diff --git a/reference/opencode-1.15.5/.github/workflows/close-prs.yml b/reference/opencode-1.15.5/.github/workflows/close-prs.yml deleted file mode 100755 index a1e603a..0000000 --- a/reference/opencode-1.15.5/.github/workflows/close-prs.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: close-prs - -on: - schedule: - - cron: "0 22 * * *" # Daily at 10:00 PM UTC - workflow_dispatch: - inputs: - dry-run: - description: "Log matching PRs without closing them" - type: boolean - default: true - max-close: - description: "Maximum matching PRs to close" - type: string - required: false - default: "50" - -jobs: - close: - runs-on: ubuntu-latest - timeout-minutes: 240 - permissions: - contents: read - issues: write - pull-requests: write - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - with: - bun-version: latest - - - name: Close old PRs without enough positive reactions - env: - GITHUB_TOKEN: ${{ github.token }} - run: | - max_close="${{ inputs['max-close'] }}" - if [ -z "$max_close" ]; then - max_close="50" - fi - - args=("--threshold" "2" "--age-months" "1" "--sleep-ms" "20000" "--max-close" "$max_close") - - if [ "${{ github.event_name }}" = "schedule" ]; then - args+=("--execute") - elif [ "${{ inputs['dry-run'] }}" = "false" ]; then - args+=("--execute") - fi - - bun script/github/close-prs.ts "${args[@]}" diff --git a/reference/opencode-1.15.5/.github/workflows/compliance-close.yml b/reference/opencode-1.15.5/.github/workflows/compliance-close.yml deleted file mode 100755 index 14e6870..0000000 --- a/reference/opencode-1.15.5/.github/workflows/compliance-close.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: compliance-close - -on: - schedule: - # Run every 30 minutes to check for expired compliance windows - - cron: "*/30 * * * *" - workflow_dispatch: - -permissions: - contents: read - issues: write - pull-requests: write - -jobs: - close-non-compliant: - runs-on: ubuntu-latest - steps: - - name: Close non-compliant issues and PRs after 2 hours - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 - with: - script: | - const { data: items } = await github.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - labels: 'needs:compliance', - state: 'open', - per_page: 100, - }); - - if (items.length === 0) { - core.info('No open issues/PRs with needs:compliance label'); - return; - } - - const now = Date.now(); - const twoHours = 2 * 60 * 60 * 1000; - - for (const item of items) { - const isPR = !!item.pull_request; - const kind = isPR ? 'PR' : 'issue'; - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: item.number, - }); - - const complianceComment = comments.find(c => c.body.includes('')); - if (!complianceComment) continue; - - const commentAge = now - new Date(complianceComment.created_at).getTime(); - if (commentAge < twoHours) { - core.info(`${kind} #${item.number} still within 2-hour window (${Math.round(commentAge / 60000)}m elapsed)`); - continue; - } - - const closeMessage = isPR - ? 'This pull request has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 2-hour window.\n\nFeel free to open a new pull request that follows our guidelines.' - : 'This issue has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 2-hour window.\n\nFeel free to open a new issue that follows our issue templates.'; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: item.number, - body: closeMessage, - }); - - try { - await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: item.number, - name: 'needs:compliance', - }); - } catch (e) {} - - if (isPR) { - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: item.number, - state: 'closed', - }); - } else { - await github.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: item.number, - state: 'closed', - state_reason: 'not_planned', - }); - } - - core.info(`Closed non-compliant ${kind} #${item.number} after 2-hour window`); - } diff --git a/reference/opencode-1.15.5/.github/workflows/containers.yml b/reference/opencode-1.15.5/.github/workflows/containers.yml deleted file mode 100755 index 15bf078..0000000 --- a/reference/opencode-1.15.5/.github/workflows/containers.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: containers - -on: - push: - branches: - - dev - paths: - - packages/containers/** - - .github/workflows/containers.yml - - package.json - workflow_dispatch: - -permissions: - contents: read - packages: write - -jobs: - build: - runs-on: blacksmith-4vcpu-ubuntu-2404 - env: - REGISTRY: ghcr.io/${{ github.repository_owner }} - TAG: "24.04" - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - uses: ./.github/actions/setup-bun - - - name: Set up QEMU - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - - - name: Login to GHCR - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push containers - run: bun ./packages/containers/script/build.ts --push - env: - REGISTRY: ${{ env.REGISTRY }} - TAG: ${{ env.TAG }} diff --git a/reference/opencode-1.15.5/.github/workflows/deploy.yml b/reference/opencode-1.15.5/.github/workflows/deploy.yml deleted file mode 100755 index 7b4f53a..0000000 --- a/reference/opencode-1.15.5/.github/workflows/deploy.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: deploy - -on: - push: - branches: - - dev - - production - workflow_dispatch: - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: ./.github/actions/setup-bun - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "24" - - # Workaround for Pulumi version conflict: - # GitHub runners have Pulumi 3.212.0+ pre-installed, which removed the -root flag - # from pulumi-language-nodejs (see https://github.com/pulumi/pulumi/pull/21065). - # SST 3.17.x uses Pulumi SDK 3.210.0 which still passes -root, causing a conflict. - # Removing the system language plugin forces SST to use its bundled compatible version. - # TODO: Remove when sst supports Pulumi >3.210.0 - - name: Fix Pulumi version conflict - run: sudo rm -f /usr/local/bin/pulumi-language-nodejs - - - run: bun sst deploy --stage=${{ github.ref_name }} - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }} - PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }} - STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }} - HONEYCOMB_API_KEY: ${{ secrets.HONEYCOMB_API_KEY }} - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ vars.SENTRY_ORG }} - SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }} - SENTRY_RELEASE: web@${{ github.sha }} - VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }} - VITE_SENTRY_RELEASE: web@${{ github.sha }} diff --git a/reference/opencode-1.15.5/.github/workflows/docs-locale-sync.yml b/reference/opencode-1.15.5/.github/workflows/docs-locale-sync.yml deleted file mode 100755 index 5f921e8..0000000 --- a/reference/opencode-1.15.5/.github/workflows/docs-locale-sync.yml +++ /dev/null @@ -1,100 +0,0 @@ -name: docs-locale-sync - -on: - push: - branches: - - dev - paths: - - packages/web/src/content/docs/*.mdx - -jobs: - sync-locales: - if: false - #if: github.actor != 'opencode-agent[bot]' - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - persist-credentials: false - fetch-depth: 0 - ref: ${{ github.ref_name }} - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Compute changed English docs - id: changes - run: | - FILES=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" -- ':(glob)packages/web/src/content/docs/*.mdx' || true) - if [ -z "$FILES" ]; then - echo "has_changes=false" >> "$GITHUB_OUTPUT" - echo "No English docs changed in push range" - exit 0 - fi - echo "has_changes=true" >> "$GITHUB_OUTPUT" - { - echo "files<> "$GITHUB_OUTPUT" - - - name: Install OpenCode - if: steps.changes.outputs.has_changes == 'true' - run: curl -fsSL https://opencode.ai/install | bash - - - name: Sync locale docs with OpenCode - if: steps.changes.outputs.has_changes == 'true' - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - OPENCODE_CONFIG_CONTENT: | - { - "permission": { - "*": "deny", - "read": "allow", - "edit": "allow", - "glob": "allow", - "task": "allow" - } - } - run: | - opencode run --agent docs --model opencode/gpt-5.3-codex <<'EOF' - Update localized docs to match the latest English docs changes. - - Changed English doc files: - - ${{ steps.changes.outputs.files }} - - - Requirements: - 1. Update all relevant locale docs under packages/web/src/content/docs// so they reflect these English page changes. - 2. You MUST use the Task tool for translation work and launch subagents with subagent_type `translator` (defined in .opencode/agent/translator.md). - 3. Do not translate directly in the primary agent. Use translator subagent output as the source for locale text updates. - 4. Run translator subagent Task calls in parallel whenever file/locale translation work is independent. - 5. Use only the minimum tools needed for this task (read/glob, file edits, and translator Task). Do not use shell, web, search, or GitHub tools for translation work. - 6. Preserve frontmatter keys, internal links, code blocks, and existing locale-specific metadata unless the English change requires an update. - 7. Keep locale docs structure aligned with their corresponding English pages. - 8. Do not modify English source docs in packages/web/src/content/docs/*.mdx. - 9. If no locale updates are needed, make no changes. - EOF - - - name: Commit and push locale docs updates - if: steps.changes.outputs.has_changes == 'true' - run: | - if [ -z "$(git status --porcelain)" ]; then - echo "No locale docs changes to commit" - exit 0 - fi - git add -A - git commit -m "docs(i18n): sync locale docs from english changes" - git pull --rebase --autostash origin "$GITHUB_REF_NAME" - git push origin HEAD:"$GITHUB_REF_NAME" diff --git a/reference/opencode-1.15.5/.github/workflows/docs-update.yml b/reference/opencode-1.15.5/.github/workflows/docs-update.yml deleted file mode 100755 index 4767dec..0000000 --- a/reference/opencode-1.15.5/.github/workflows/docs-update.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: docs-update - -on: - schedule: - - cron: "0 */12 * * *" - workflow_dispatch: - -env: - LOOKBACK_HOURS: 4 - -jobs: - update-docs: - if: github.repository == 'sst/opencode' - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - id-token: write - contents: write - pull-requests: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 0 # Fetch full history to access commits - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Get recent commits - id: commits - run: | - COMMITS=$(git log --since="${{ env.LOOKBACK_HOURS }} hours ago" --pretty=format:"- %h %s" 2>/dev/null || echo "") - if [ -z "$COMMITS" ]; then - echo "No commits in the last ${{ env.LOOKBACK_HOURS }} hours" - echo "has_commits=false" >> $GITHUB_OUTPUT - else - echo "has_commits=true" >> $GITHUB_OUTPUT - { - echo "list<> $GITHUB_OUTPUT - fi - - - name: Run opencode - if: steps.commits.outputs.has_commits == 'true' - uses: sst/opencode/github@2c14fc5586fe0b88e5c04732d2e846769cc35671 # latest - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - with: - model: opencode/gpt-5.2 - agent: docs - prompt: | - Review the following commits from the last ${{ env.LOOKBACK_HOURS }} hours and identify any new features that may need documentation. - - - ${{ steps.commits.outputs.list }} - - - Steps: - 1. For each commit that looks like a new feature or significant change: - - Read the changed files to understand what was added - - Check if the feature is already documented in packages/web/src/content/docs/* - 2. If you find undocumented features: - - Update the relevant documentation files in packages/web/src/content/docs/* - - Follow the existing documentation style and structure - - Make sure to document the feature clearly with examples where appropriate - 3. If all new features are already documented, report that no updates are needed - 4. If you are creating a new documentation file be sure to update packages/web/astro.config.mjs too. - - Focus on user-facing features and API changes. Skip internal refactors, bug fixes, and test updates unless they affect user-facing behavior. - Don't feel the need to document every little thing. It is perfectly okay to make 0 changes at all. - Try to keep documentation only for large features or changes that already have a good spot to be documented. diff --git a/reference/opencode-1.15.5/.github/workflows/duplicate-issues.yml b/reference/opencode-1.15.5/.github/workflows/duplicate-issues.yml deleted file mode 100755 index 4648a2d..0000000 --- a/reference/opencode-1.15.5/.github/workflows/duplicate-issues.yml +++ /dev/null @@ -1,177 +0,0 @@ -name: duplicate-issues - -on: - issues: - types: [opened, edited] - -jobs: - check-duplicates: - if: github.event.action == 'opened' - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - issues: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 1 - - - uses: ./.github/actions/setup-bun - - - name: Install opencode - run: curl -fsSL https://opencode.ai/install | bash - - - name: Check duplicates and compliance - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: | - { - "bash": { - "*": "deny", - "gh issue*": "allow" - }, - "webfetch": "deny" - } - run: | - opencode run -m opencode/claude-sonnet-4-6 "A new issue has been created: - - Issue number: ${{ github.event.issue.number }} - - Lookup this issue with gh issue view ${{ github.event.issue.number }}. - - You have TWO tasks. Perform both, then post a SINGLE comment (if needed). - - --- - - TASK 1: CONTRIBUTING GUIDELINES COMPLIANCE CHECK - - Check whether the issue follows our contributing guidelines and issue templates. - - This project has three issue templates that every issue MUST use one of: - - 1. Bug Report - requires a Description field with real content - 2. Feature Request - requires a verification checkbox and description, title should start with [FEATURE]: - 3. Question - requires the Question field with real content - - Additionally check: - - No AI-generated walls of text (long, AI-generated descriptions are not acceptable) - - The issue has real content, not just template placeholder text left unchanged - - Bug reports should include some context about how to reproduce - - Feature requests should explain the problem or need - - We want to push for having the user provide system description & information - - Do NOT be nitpicky about optional fields. Only flag real problems like: no template used, required fields empty or placeholder text only, obviously AI-generated walls of text, or completely empty/nonsensical content. - - --- - - TASK 2: DUPLICATE CHECK - - Search through existing issues (excluding #${{ github.event.issue.number }}) to find potential duplicates. - Consider: - 1. Similar titles or descriptions - 2. Same error messages or symptoms - 3. Related functionality or components - 4. Similar feature requests - - Additionally, if the issue mentions keybinds, keyboard shortcuts, or key bindings, note the pinned keybinds issue #4997. - - --- - - POSTING YOUR COMMENT: - - Based on your findings, post a SINGLE comment on issue #${{ github.event.issue.number }}. Build the comment as follows: - - If the issue is NOT compliant, start the comment with: - - Then explain what needs to be fixed and that they have 2 hours to edit the issue before it is automatically closed. Also add the label needs:compliance to the issue using: gh issue edit ${{ github.event.issue.number }} --add-label needs:compliance - - If duplicates were found, include a section about potential duplicates with links. - - If the issue mentions keybinds/keyboard shortcuts, include a note about #4997. - - If the issue IS compliant AND no duplicates were found AND no keybind reference, do NOT comment at all. - - Use this format for the comment: - - [If not compliant:] - - This issue doesn't fully meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md). - - **What needs to be fixed:** - - [specific reasons] - - Please edit this issue to address the above within **2 hours**, or it will be automatically closed. - - [If duplicates found, add:] - --- - This issue might be a duplicate of existing issues. Please check: - - #[issue_number]: [brief description of similarity] - - [If keybind-related, add:] - For keybind-related issues, please also check our pinned keybinds documentation: #4997 - - [End with if not compliant:] - If you believe this was flagged incorrectly, please let a maintainer know. - - Remember: post at most ONE comment combining all findings. If everything is fine, post nothing." - - recheck-compliance: - if: github.event.action == 'edited' && contains(github.event.issue.labels.*.name, 'needs:compliance') - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - issues: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 1 - - - uses: ./.github/actions/setup-bun - - - name: Install opencode - run: curl -fsSL https://opencode.ai/install | bash - - - name: Recheck compliance - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: | - { - "bash": { - "*": "deny", - "gh issue*": "allow" - }, - "webfetch": "deny" - } - run: | - opencode run -m opencode/claude-sonnet-4-6 "Issue #${{ github.event.issue.number }} was previously flagged as non-compliant and has been edited. - - Lookup this issue with gh issue view ${{ github.event.issue.number }}. - - Re-check whether the issue now follows our contributing guidelines and issue templates. - - This project has three issue templates that every issue MUST use one of: - - 1. Bug Report - requires a Description field with real content - 2. Feature Request - requires a verification checkbox and description, title should start with [FEATURE]: - 3. Question - requires the Question field with real content - - Additionally check: - - No AI-generated walls of text (long, AI-generated descriptions are not acceptable) - - The issue has real content, not just template placeholder text left unchanged - - Bug reports should include some context about how to reproduce - - Feature requests should explain the problem or need - - We want to push for having the user provide system description & information - - Do NOT be nitpicky about optional fields. Only flag real problems like: no template used, required fields empty or placeholder text only, obviously AI-generated walls of text, or completely empty/nonsensical content. - - If the issue is NOW compliant: - 1. Remove the needs:compliance label: gh issue edit ${{ github.event.issue.number }} --remove-label needs:compliance - 2. Find and delete the previous compliance comment (the one containing ) using: gh api repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments --jq '.[] | select(.body | contains(\"\")) | .id' then delete it with: gh api -X DELETE repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments/{id} - 3. Post a short comment thanking them for updating the issue. - - If the issue is STILL not compliant: - Post a comment explaining what still needs to be fixed. Keep the needs:compliance label." diff --git a/reference/opencode-1.15.5/.github/workflows/generate.yml b/reference/opencode-1.15.5/.github/workflows/generate.yml deleted file mode 100755 index 324cfec..0000000 --- a/reference/opencode-1.15.5/.github/workflows/generate.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: generate - -on: - push: - branches: - - dev - -jobs: - generate: - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: write - pull-requests: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Generate - run: ./script/generate.ts - - - name: Commit and push - run: | - if [ -z "$(git status --porcelain)" ]; then - echo "No changes to commit" - exit 0 - fi - git add -A - git commit -m "chore: generate" --allow-empty - git push origin HEAD:${{ github.ref_name }} --no-verify - # if ! git push origin HEAD:${{ github.event.pull_request.head.ref || github.ref_name }} --no-verify; then - # echo "" - # echo "============================================" - # echo "Failed to push generated code." - # echo "Please run locally and push:" - # echo "" - # echo " ./script/generate.ts" - # echo " git add -A && git commit -m \"chore: generate\" && git push" - # echo "" - # echo "============================================" - # exit 1 - # fi diff --git a/reference/opencode-1.15.5/.github/workflows/nix-eval.yml b/reference/opencode-1.15.5/.github/workflows/nix-eval.yml deleted file mode 100755 index 7533269..0000000 --- a/reference/opencode-1.15.5/.github/workflows/nix-eval.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: nix-eval - -on: - push: - branches: [dev] - pull_request: - branches: [dev] - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - nix-eval: - runs-on: blacksmith-4vcpu-ubuntu-2404 - timeout-minutes: 15 - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Setup Nix - uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34 - - - name: Evaluate flake outputs (all systems) - run: | - set -euo pipefail - nix --version - - echo "=== Flake metadata ===" - nix flake metadata - - echo "" - echo "=== Flake structure ===" - nix flake show --all-systems - - SYSTEMS="x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin" - PACKAGES="opencode" - # TODO: move 'desktop' to PACKAGES when #11755 is fixed - OPTIONAL_PACKAGES="desktop" - - echo "" - echo "=== Evaluating packages for all systems ===" - for system in $SYSTEMS; do - echo "" - echo "--- $system ---" - for pkg in $PACKAGES; do - printf " %s: " "$pkg" - if output=$(nix eval ".#packages.$system.$pkg.drvPath" --raw 2>&1); then - echo "✓" - else - echo "✗" - echo "::error::Evaluation failed for packages.$system.$pkg" - echo "$output" - exit 1 - fi - done - done - - echo "" - echo "=== Evaluating optional packages ===" - for system in $SYSTEMS; do - echo "" - echo "--- $system ---" - for pkg in $OPTIONAL_PACKAGES; do - printf " %s: " "$pkg" - if output=$(nix eval ".#packages.$system.$pkg.drvPath" --raw 2>&1); then - echo "✓" - else - echo "✗" - echo "::warning::Evaluation failed for packages.$system.$pkg" - echo "$output" - fi - done - done - - echo "" - echo "=== Evaluating devShells for all systems ===" - for system in $SYSTEMS; do - printf "%s: " "$system" - if output=$(nix eval ".#devShells.$system.default.drvPath" --raw 2>&1); then - echo "✓" - else - echo "✗" - echo "::error::Evaluation failed for devShells.$system.default" - echo "$output" - exit 1 - fi - done - - echo "" - echo "=== All evaluations passed ===" diff --git a/reference/opencode-1.15.5/.github/workflows/nix-hashes.yml b/reference/opencode-1.15.5/.github/workflows/nix-hashes.yml deleted file mode 100755 index 085f889..0000000 --- a/reference/opencode-1.15.5/.github/workflows/nix-hashes.yml +++ /dev/null @@ -1,152 +0,0 @@ -name: nix-hashes - -permissions: - contents: write - -on: - workflow_dispatch: - push: - branches: [dev, beta] - paths: - - "bun.lock" - - "package.json" - - "packages/*/package.json" - - "flake.lock" - - "nix/node_modules.nix" - - "nix/scripts/**" - - "patches/**" - - ".github/workflows/nix-hashes.yml" - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - # Native runners required: bun install cross-compilation flags (--os/--cpu) - # do not produce byte-identical node_modules as native installs. - compute-hash: - strategy: - fail-fast: false - matrix: - include: - - system: x86_64-linux - runner: blacksmith-4vcpu-ubuntu-2404 - - system: aarch64-linux - runner: blacksmith-4vcpu-ubuntu-2404-arm - - system: x86_64-darwin - runner: macos-15-intel - - system: aarch64-darwin - runner: macos-latest - runs-on: ${{ matrix.runner }} - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Setup Nix - uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34 - - - name: Compute node_modules hash - id: hash - env: - SYSTEM: ${{ matrix.system }} - run: | - set -euo pipefail - - BUILD_LOG=$(mktemp) - trap 'rm -f "$BUILD_LOG"' EXIT - - # Build with fakeHash to trigger hash mismatch and reveal correct hash - nix build ".#packages.${SYSTEM}.node_modules_updater" --no-link 2>&1 | tee "$BUILD_LOG" || true - - # Extract hash from build log with portability - HASH="$(nix run --inputs-from . nixpkgs#gnugrep -- -oP 'got:\s*\Ksha256-[A-Za-z0-9+/=]+' "$BUILD_LOG" | tail -n1 || true)" - - if [ -z "$HASH" ]; then - echo "::error::Failed to compute hash for ${SYSTEM}" - cat "$BUILD_LOG" - exit 1 - fi - - echo "$HASH" > hash.txt - echo "Computed hash for ${SYSTEM}: $HASH" - - - name: Upload hash - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: hash-${{ matrix.system }} - path: hash.txt - retention-days: 1 - - update-hashes: - needs: compute-hash - if: github.event_name != 'pull_request' - runs-on: blacksmith-4vcpu-ubuntu-2404 - - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - persist-credentials: false - fetch-depth: 0 - ref: ${{ github.ref_name }} - - - name: Setup git committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Pull latest changes - run: | - git pull --rebase --autostash origin "$GITHUB_REF_NAME" - - - name: Download hash artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - path: hashes - pattern: hash-* - - - name: Update hashes.json - run: | - set -euo pipefail - - HASH_FILE="nix/hashes.json" - - [ -f "$HASH_FILE" ] || echo '{"nodeModules":{}}' > "$HASH_FILE" - - for SYSTEM in x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin; do - FILE="hashes/hash-${SYSTEM}/hash.txt" - if [ -f "$FILE" ]; then - HASH="$(tr -d '[:space:]' < "$FILE")" - echo "${SYSTEM}: ${HASH}" - jq --arg sys "$SYSTEM" --arg h "$HASH" '.nodeModules[$sys] = $h' "$HASH_FILE" > tmp.json - mv tmp.json "$HASH_FILE" - else - echo "::warning::Missing hash for ${SYSTEM}" - fi - done - - cat "$HASH_FILE" - - - name: Commit changes - run: | - set -euo pipefail - - HASH_FILE="nix/hashes.json" - - if [ -z "$(git status --short -- "$HASH_FILE")" ]; then - echo "No changes to commit" - echo "### Nix hashes" >> "$GITHUB_STEP_SUMMARY" - echo "Status: no changes" >> "$GITHUB_STEP_SUMMARY" - exit 0 - fi - - git add "$HASH_FILE" - git commit -m "chore: update nix node_modules hashes" - - git pull --rebase --autostash origin "$GITHUB_REF_NAME" - git push origin HEAD:"$GITHUB_REF_NAME" - - echo "### Nix hashes" >> "$GITHUB_STEP_SUMMARY" - echo "Status: committed $(git rev-parse --short HEAD)" >> "$GITHUB_STEP_SUMMARY" diff --git a/reference/opencode-1.15.5/.github/workflows/notify-discord.yml b/reference/opencode-1.15.5/.github/workflows/notify-discord.yml deleted file mode 100755 index 0b2b1cd..0000000 --- a/reference/opencode-1.15.5/.github/workflows/notify-discord.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: notify-discord - -on: - release: - types: [released] # fires when a draft release is published - -jobs: - notify: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - name: Send nicely-formatted embed to Discord - uses: SethCohen/github-releases-to-discord@24d166886aee4646d448c8a389ff9e1ebcab3682 # v1.20.0 - with: - webhook_url: ${{ secrets.DISCORD_WEBHOOK }} diff --git a/reference/opencode-1.15.5/.github/workflows/opencode.yml b/reference/opencode-1.15.5/.github/workflows/opencode.yml deleted file mode 100755 index 3469c21..0000000 --- a/reference/opencode-1.15.5/.github/workflows/opencode.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: opencode - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - -jobs: - opencode: - if: | - contains(github.event.comment.body, ' /oc') || - startsWith(github.event.comment.body, '/oc') || - contains(github.event.comment.body, ' /opencode') || - startsWith(github.event.comment.body, '/opencode') - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - id-token: write - contents: read - pull-requests: read - issues: read - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - uses: ./.github/actions/setup-bun - - - name: Run opencode - uses: anomalyco/opencode/github@2c14fc5586fe0b88e5c04732d2e846769cc35671 # latest - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - OPENCODE_PERMISSION: '{"bash": "deny"}' - with: - model: opencode/claude-opus-4-5 diff --git a/reference/opencode-1.15.5/.github/workflows/pr-management.yml b/reference/opencode-1.15.5/.github/workflows/pr-management.yml deleted file mode 100755 index b6aa4e5..0000000 --- a/reference/opencode-1.15.5/.github/workflows/pr-management.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: pr-management - -on: - pull_request_target: - types: [opened] - -jobs: - check-duplicates: - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - pull-requests: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 1 - - - name: Check team membership - id: team-check - run: | - LOGIN="${{ github.event.pull_request.user.login }}" - if [ "$LOGIN" = "opencode-agent[bot]" ] || grep -qxF "$LOGIN" .github/TEAM_MEMBERS; then - echo "is_team=true" >> "$GITHUB_OUTPUT" - echo "Skipping: $LOGIN is a team member or bot" - else - echo "is_team=false" >> "$GITHUB_OUTPUT" - fi - - - name: Setup Bun - if: steps.team-check.outputs.is_team != 'true' - uses: ./.github/actions/setup-bun - - - name: Install dependencies - if: steps.team-check.outputs.is_team != 'true' - run: bun install - - - name: Install opencode - if: steps.team-check.outputs.is_team != 'true' - run: curl -fsSL https://opencode.ai/install | bash - - - name: Build prompt - if: steps.team-check.outputs.is_team != 'true' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - { - echo "Check for duplicate PRs related to this new PR:" - echo "" - echo "CURRENT_PR_NUMBER: $PR_NUMBER" - echo "" - echo "Title: $(gh pr view "$PR_NUMBER" --json title --jq .title)" - echo "" - echo "Description:" - gh pr view "$PR_NUMBER" --json body --jq .body - } > pr_info.txt - - - name: Check for duplicate PRs - if: steps.team-check.outputs.is_team != 'true' - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - COMMENT=$(bun script/duplicate-pr.ts -f pr_info.txt "Check the attached file for PR details and search for duplicates") - - if [ "$COMMENT" != "No duplicate PRs found" ]; then - gh pr comment "$PR_NUMBER" --body "_The following comment was made by an LLM, it may be inaccurate:_ - - $COMMENT" - fi - - add-contributor-label: - runs-on: ubuntu-latest - permissions: - pull-requests: write - issues: write - steps: - - name: Add Contributor Label - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const isPR = !!context.payload.pull_request; - const issueNumber = isPR ? context.payload.pull_request.number : context.payload.issue.number; - const authorAssociation = isPR ? context.payload.pull_request.author_association : context.payload.issue.author_association; - - if (authorAssociation === 'CONTRIBUTOR') { - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issueNumber, - labels: ['contributor'] - }); - } diff --git a/reference/opencode-1.15.5/.github/workflows/pr-standards.yml b/reference/opencode-1.15.5/.github/workflows/pr-standards.yml deleted file mode 100755 index 0683808..0000000 --- a/reference/opencode-1.15.5/.github/workflows/pr-standards.yml +++ /dev/null @@ -1,351 +0,0 @@ -name: pr-standards - -on: - pull_request_target: - types: [opened, edited, synchronize] - -jobs: - check-standards: - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - steps: - - name: Check PR standards - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 - with: - script: | - const pr = context.payload.pull_request; - const login = pr.user.login; - - // Skip PRs older than Feb 18, 2026 at 6PM EST (Feb 19, 2026 00:00 UTC) - const cutoff = new Date('2026-02-19T00:00:00Z'); - const prCreated = new Date(pr.created_at); - if (prCreated < cutoff) { - console.log(`Skipping: PR #${pr.number} was created before cutoff (${prCreated.toISOString()})`); - return; - } - - // Check if author is a team member or bot - if (login === 'opencode-agent[bot]') return; - const { data: file } = await github.rest.repos.getContent({ - owner: context.repo.owner, - repo: context.repo.repo, - path: '.github/TEAM_MEMBERS', - ref: 'dev' - }); - const members = Buffer.from(file.content, 'base64').toString().split('\n').map(l => l.trim()).filter(Boolean); - if (members.includes(login)) { - console.log(`Skipping: ${login} is a team member`); - return; - } - - const title = pr.title; - - async function addLabel(label) { - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - labels: [label] - }); - } - - async function removeLabel(label) { - try { - await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - name: label - }); - } catch (e) { - // Label wasn't present, ignore - } - } - - async function comment(marker, body) { - const markerText = ``; - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number - }); - - const existing = comments.find(c => c.body.includes(markerText)); - if (existing) return; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: markerText + '\n' + body - }); - } - - // Step 1: Check title format - // Matches: feat:, feat(scope):, feat (scope):, etc. - const titlePattern = /^(feat|fix|docs|chore|refactor|test)\s*(\([a-zA-Z0-9-]+\))?\s*:/; - const hasValidTitle = titlePattern.test(title); - - if (!hasValidTitle) { - await addLabel('needs:title'); - await comment('title', `Hey! Your PR title \`${title}\` doesn't follow conventional commit format. - - Please update it to start with one of: - - \`feat:\` or \`feat(scope):\` new feature - - \`fix:\` or \`fix(scope):\` bug fix - - \`docs:\` or \`docs(scope):\` documentation changes - - \`chore:\` or \`chore(scope):\` maintenance tasks - - \`refactor:\` or \`refactor(scope):\` code refactoring - - \`test:\` or \`test(scope):\` adding or updating tests - - Where \`scope\` is the package name (e.g., \`app\`, \`desktop\`, \`opencode\`). - - See [CONTRIBUTING.md](../blob/dev/CONTRIBUTING.md#pr-titles) for details.`); - return; - } - - await removeLabel('needs:title'); - - // Step 2: Check for linked issue (skip for docs/refactor/feat PRs) - const skipIssueCheck = /^(docs|refactor|feat)\s*(\([a-zA-Z0-9-]+\))?\s*:/.test(title); - if (skipIssueCheck) { - await removeLabel('needs:issue'); - console.log('Skipping issue check for docs/refactor/feat PR'); - return; - } - const query = ` - query($owner: String!, $repo: String!, $number: Int!) { - repository(owner: $owner, name: $repo) { - pullRequest(number: $number) { - closingIssuesReferences(first: 1) { - totalCount - } - } - } - } - `; - - const result = await github.graphql(query, { - owner: context.repo.owner, - repo: context.repo.repo, - number: pr.number - }); - - const linkedIssues = result.repository.pullRequest.closingIssuesReferences.totalCount; - - if (linkedIssues === 0) { - await addLabel('needs:issue'); - await comment('issue', `Thanks for your contribution! - - This PR doesn't have a linked issue. All PRs must reference an existing issue. - - Please: - 1. Open an issue describing the bug/feature (if one doesn't exist) - 2. Add \`Fixes #\` or \`Closes #\` to this PR description - - See [CONTRIBUTING.md](../blob/dev/CONTRIBUTING.md#issue-first-policy) for details.`); - return; - } - - await removeLabel('needs:issue'); - console.log('PR meets all standards'); - - check-compliance: - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - steps: - - name: Check PR template compliance - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 - with: - script: | - const pr = context.payload.pull_request; - const login = pr.user.login; - - // Skip PRs older than Feb 18, 2026 at 6PM EST (Feb 19, 2026 00:00 UTC) - const cutoff = new Date('2026-02-19T00:00:00Z'); - const prCreated = new Date(pr.created_at); - if (prCreated < cutoff) { - console.log(`Skipping: PR #${pr.number} was created before cutoff (${prCreated.toISOString()})`); - return; - } - - // Check if author is a team member or bot - if (login === 'opencode-agent[bot]') return; - const { data: file } = await github.rest.repos.getContent({ - owner: context.repo.owner, - repo: context.repo.repo, - path: '.github/TEAM_MEMBERS', - ref: 'dev' - }); - const members = Buffer.from(file.content, 'base64').toString().split('\n').map(l => l.trim()).filter(Boolean); - if (members.includes(login)) { - console.log(`Skipping: ${login} is a team member`); - return; - } - - const body = pr.body || ''; - const title = pr.title; - const isDocsRefactorOrFeat = /^(docs|refactor|feat)\s*(\([a-zA-Z0-9-]+\))?\s*:/.test(title); - - const issues = []; - - // Check: template sections exist - const hasWhatSection = /### What does this PR do\?/.test(body); - const hasTypeSection = /### Type of change/.test(body); - const hasVerifySection = /### How did you verify your code works\?/.test(body); - const hasChecklistSection = /### Checklist/.test(body); - const hasIssueSection = /### Issue for this PR/.test(body); - - if (!hasWhatSection || !hasTypeSection || !hasVerifySection || !hasChecklistSection || !hasIssueSection) { - issues.push('PR description is missing required template sections. Please use the [PR template](../blob/dev/.github/pull_request_template.md).'); - } - - // Check: "What does this PR do?" has real content (not just placeholder text) - if (hasWhatSection) { - const whatMatch = body.match(/### What does this PR do\?\s*\n([\s\S]*?)(?=###|$)/); - const whatContent = whatMatch ? whatMatch[1].trim() : ''; - const placeholder = 'Please provide a description of the issue'; - const onlyPlaceholder = whatContent.includes(placeholder) && whatContent.replace(placeholder, '').replace(/[*\s]/g, '').length < 20; - if (!whatContent || onlyPlaceholder) { - issues.push('"What does this PR do?" section is empty or only contains placeholder text. Please describe your changes.'); - } - } - - // Check: at least one "Type of change" checkbox is checked - if (hasTypeSection) { - const typeMatch = body.match(/### Type of change\s*\n([\s\S]*?)(?=###|$)/); - const typeContent = typeMatch ? typeMatch[1] : ''; - const hasCheckedBox = /- \[x\]/i.test(typeContent); - if (!hasCheckedBox) { - issues.push('No "Type of change" checkbox is checked. Please select at least one.'); - } - } - - // Check: issue reference (skip for docs/refactor/feat) - if (!isDocsRefactorOrFeat && hasIssueSection) { - const issueMatch = body.match(/### Issue for this PR\s*\n([\s\S]*?)(?=###|$)/); - const issueContent = issueMatch ? issueMatch[1].trim() : ''; - const hasIssueRef = /(closes|fixes|resolves)\s+#\d+/i.test(issueContent) || /#\d+/.test(issueContent); - if (!hasIssueRef) { - issues.push('No issue referenced. Please add `Closes #` linking to the relevant issue.'); - } - } - - // Check: "How did you verify" has content - if (hasVerifySection) { - const verifyMatch = body.match(/### How did you verify your code works\?\s*\n([\s\S]*?)(?=###|$)/); - const verifyContent = verifyMatch ? verifyMatch[1].trim() : ''; - if (!verifyContent) { - issues.push('"How did you verify your code works?" section is empty. Please explain how you tested.'); - } - } - - // Check: checklist boxes are checked - if (hasChecklistSection) { - const checklistMatch = body.match(/### Checklist\s*\n([\s\S]*?)(?=###|$)/); - const checklistContent = checklistMatch ? checklistMatch[1] : ''; - const unchecked = (checklistContent.match(/- \[ \]/g) || []).length; - const checked = (checklistContent.match(/- \[x\]/gi) || []).length; - if (checked < 2) { - issues.push('Not all checklist items are checked. Please confirm you have tested locally and have not included unrelated changes.'); - } - } - - // Helper functions - async function addLabel(label) { - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - labels: [label] - }); - } - - async function removeLabel(label) { - try { - await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - name: label - }); - } catch (e) {} - } - - const hasComplianceLabel = pr.labels.some(l => l.name === 'needs:compliance'); - - if (issues.length > 0) { - // Non-compliant - if (!hasComplianceLabel) { - await addLabel('needs:compliance'); - } - - const marker = ''; - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number - }); - const existing = comments.find(c => c.body.includes(marker)); - - const body_text = `${marker} - This PR doesn't fully meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) and [PR template](../blob/dev/.github/pull_request_template.md). - - **What needs to be fixed:** - ${issues.map(i => `- ${i}`).join('\n')} - - Please edit this PR description to address the above within **2 hours**, or it will be automatically closed. - - If you believe this was flagged incorrectly, please let a maintainer know.`; - - if (existing) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: body_text - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: body_text - }); - } - - console.log(`PR #${pr.number} is non-compliant: ${issues.join(', ')}`); - } else if (hasComplianceLabel) { - // Was non-compliant, now fixed - await removeLabel('needs:compliance'); - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number - }); - const marker = ''; - const existing = comments.find(c => c.body.includes(marker)); - if (existing) { - await github.rest.issues.deleteComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id - }); - } - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: 'Thanks for updating your PR! It now meets our contributing guidelines. :+1:' - }); - - console.log(`PR #${pr.number} is now compliant, label removed`); - } else { - console.log(`PR #${pr.number} is compliant`); - } diff --git a/reference/opencode-1.15.5/.github/workflows/publish-github-action.yml b/reference/opencode-1.15.5/.github/workflows/publish-github-action.yml deleted file mode 100755 index e5ca91b..0000000 --- a/reference/opencode-1.15.5/.github/workflows/publish-github-action.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: publish-github-action - -on: - workflow_dispatch: - push: - tags: - - "github-v*.*.*" - - "!github-v1" - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -permissions: - contents: write - -jobs: - publish: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - with: - fetch-depth: 0 - - - run: git fetch --force --tags - - - name: Publish - run: | - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" - ./script/publish - working-directory: ./github diff --git a/reference/opencode-1.15.5/.github/workflows/publish-vscode.yml b/reference/opencode-1.15.5/.github/workflows/publish-vscode.yml deleted file mode 100755 index 00c7e26..0000000 --- a/reference/opencode-1.15.5/.github/workflows/publish-vscode.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: publish-vscode - -on: - workflow_dispatch: - push: - tags: - - "vscode-v*.*.*" - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -permissions: - contents: write - -jobs: - publish: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - with: - fetch-depth: 0 - - - uses: ./.github/actions/setup-bun - - - run: git fetch --force --tags - - run: bun install -g @vscode/vsce - - - name: Install extension dependencies - run: bun install - working-directory: ./sdks/vscode - - - name: Publish - run: | - ./script/publish - working-directory: ./sdks/vscode - env: - VSCE_PAT: ${{ secrets.VSCE_PAT }} - OPENVSX_TOKEN: ${{ secrets.OPENVSX_TOKEN }} diff --git a/reference/opencode-1.15.5/.github/workflows/publish.yml b/reference/opencode-1.15.5/.github/workflows/publish.yml deleted file mode 100755 index 9887cbe..0000000 --- a/reference/opencode-1.15.5/.github/workflows/publish.yml +++ /dev/null @@ -1,491 +0,0 @@ -name: publish -run-name: "${{ format('release {0}', inputs.bump) }}" - -on: - push: - branches: - - ci - - dev - - beta - - fix/npm-native-binary-install - - snapshot-* - workflow_dispatch: - inputs: - bump: - description: "Bump major, minor, or patch" - required: false - type: choice - options: - - major - - minor - - patch - version: - description: "Override version (optional)" - required: false - type: string - -concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.version || inputs.bump }} - -permissions: - id-token: write - contents: write - packages: write - -jobs: - version: - runs-on: blacksmith-4vcpu-ubuntu-2404 - if: github.repository == 'anomalyco/opencode' - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - with: - fetch-depth: 0 - - - uses: ./.github/actions/setup-bun - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Install OpenCode - if: inputs.bump || inputs.version - run: bun i -g opencode-ai - - - id: version - run: | - ./script/version.ts - env: - GH_TOKEN: ${{ steps.committer.outputs.token }} - OPENCODE_BUMP: ${{ inputs.bump }} - OPENCODE_VERSION: ${{ inputs.version }} - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GH_REPO: ${{ (github.ref_name == 'beta' && 'anomalyco/opencode-beta') || github.repository }} - outputs: - version: ${{ steps.version.outputs.version }} - release: ${{ steps.version.outputs.release }} - tag: ${{ steps.version.outputs.tag }} - repo: ${{ steps.version.outputs.repo }} - - build-cli: - needs: version - runs-on: blacksmith-4vcpu-ubuntu-2404 - if: github.repository == 'anomalyco/opencode' - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - with: - fetch-tags: true - - - uses: ./.github/actions/setup-bun - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Build - id: build - run: | - ./packages/opencode/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }} - env: - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - OPENCODE_RELEASE: ${{ needs.version.outputs.release }} - GH_REPO: ${{ needs.version.outputs.repo }} - GH_TOKEN: ${{ steps.committer.outputs.token }} - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: opencode-cli - path: | - packages/opencode/dist/opencode-darwin* - packages/opencode/dist/opencode-linux* - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: opencode-cli-windows - path: packages/opencode/dist/opencode-windows* - outputs: - version: ${{ needs.version.outputs.version }} - - sign-cli-windows: - needs: - - build-cli - - version - runs-on: blacksmith-4vcpu-windows-2025 - if: github.repository == 'anomalyco/opencode' - env: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} - AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }} - AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: opencode-cli-windows - path: packages/opencode/dist - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Azure login - uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0 - with: - client-id: ${{ env.AZURE_CLIENT_ID }} - tenant-id: ${{ env.AZURE_TENANT_ID }} - subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }} - - - uses: azure/artifact-signing-action@b443cf8ea4124818d2ea9f043cba29fc3ec47b16 # v1.2.0 - with: - endpoint: ${{ env.AZURE_TRUSTED_SIGNING_ENDPOINT }} - signing-account-name: ${{ env.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} - certificate-profile-name: ${{ env.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }} - files: | - ${{ github.workspace }}\packages\opencode\dist\opencode-windows-arm64\bin\opencode.exe - ${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64\bin\opencode.exe - ${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64-baseline\bin\opencode.exe - exclude-environment-credential: true - exclude-workload-identity-credential: true - exclude-managed-identity-credential: true - exclude-shared-token-cache-credential: true - exclude-visual-studio-credential: true - exclude-visual-studio-code-credential: true - exclude-azure-cli-credential: false - exclude-azure-powershell-credential: true - exclude-azure-developer-cli-credential: true - exclude-interactive-browser-credential: true - - - name: Verify Windows CLI signatures - shell: pwsh - run: | - $files = @( - "${{ github.workspace }}\packages\opencode\dist\opencode-windows-arm64\bin\opencode.exe", - "${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64\bin\opencode.exe", - "${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64-baseline\bin\opencode.exe" - ) - - foreach ($file in $files) { - $sig = Get-AuthenticodeSignature $file - if ($sig.Status -ne "Valid") { - throw "Invalid signature for ${file}: $($sig.Status)" - } - } - - - name: Repack Windows CLI archives - working-directory: packages/opencode/dist - shell: pwsh - run: | - Compress-Archive -Path "opencode-windows-arm64\bin\*" -DestinationPath "opencode-windows-arm64.zip" -Force - Compress-Archive -Path "opencode-windows-x64\bin\*" -DestinationPath "opencode-windows-x64.zip" -Force - Compress-Archive -Path "opencode-windows-x64-baseline\bin\*" -DestinationPath "opencode-windows-x64-baseline.zip" -Force - - - name: Upload signed Windows CLI release assets - if: needs.version.outputs.release != '' - shell: pwsh - env: - GH_TOKEN: ${{ steps.committer.outputs.token }} - run: | - gh release upload "v${{ needs.version.outputs.version }}" ` - "${{ github.workspace }}\packages\opencode\dist\opencode-windows-arm64.zip" ` - "${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64.zip" ` - "${{ github.workspace }}\packages\opencode\dist\opencode-windows-x64-baseline.zip" ` - --clobber ` - --repo "${{ needs.version.outputs.repo }}" - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: opencode-cli-signed-windows - path: | - packages/opencode/dist/opencode-windows-arm64 - packages/opencode/dist/opencode-windows-x64 - packages/opencode/dist/opencode-windows-x64-baseline - - build-electron: - needs: - - build-cli - - version - if: github.repository == 'anomalyco/opencode' - continue-on-error: false - env: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} - AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }} - AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} - strategy: - fail-fast: false - matrix: - settings: - - host: macos-26-intel - target: x86_64-apple-darwin - platform_flag: --mac --x64 - bun_install_flags: --os=darwin --cpu=x64 - - host: macos-26 - target: aarch64-apple-darwin - platform_flag: --mac --arm64 - bun_install_flags: --os=darwin --cpu=arm64 - # github-hosted: blacksmith lacks ARM64 MSVC cross-compilation toolchain - - host: "windows-2025" - target: aarch64-pc-windows-msvc - platform_flag: --win --arm64 - - host: "blacksmith-4vcpu-windows-2025" - target: x86_64-pc-windows-msvc - platform_flag: --win - - host: "blacksmith-4vcpu-ubuntu-2404" - target: x86_64-unknown-linux-gnu - platform_flag: --linux - - host: "blacksmith-4vcpu-ubuntu-2404-arm" - target: aarch64-unknown-linux-gnu - platform_flag: --linux --arm64 - runs-on: ${{ matrix.settings.host }} - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0 - if: runner.os == 'macOS' - with: - keychain: build - p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }} - p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - - - name: Setup Apple API Key - if: runner.os == 'macOS' - run: echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8 - - - uses: ./.github/actions/setup-bun - with: - install-flags: ${{ matrix.settings.bun_install_flags }} - - - name: Azure login - if: runner.os == 'Windows' - uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0 - with: - client-id: ${{ env.AZURE_CLIENT_ID }} - tenant-id: ${{ env.AZURE_TENANT_ID }} - subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }} - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "24" - - - name: Cache apt packages - if: contains(matrix.settings.host, 'ubuntu') - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: ~/apt-cache - key: ${{ runner.os }}-${{ matrix.settings.target }}-apt-electron-${{ hashFiles('.github/workflows/publish.yml') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.settings.target }}-apt-electron- - - - name: Install dependencies (ubuntu only) - if: contains(matrix.settings.host, 'ubuntu') - run: | - mkdir -p ~/apt-cache && chmod -R a+rw ~/apt-cache - sudo apt-get update - sudo apt-get install -y --no-install-recommends -o dir::cache::archives="$HOME/apt-cache" rpm - sudo chmod -R a+rw ~/apt-cache - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Prepare - run: bun ./scripts/prepare.ts - working-directory: packages/desktop - env: - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} - OPENCODE_CLI_ARTIFACT: ${{ (runner.os == 'Windows' && 'opencode-cli-windows') || 'opencode-cli' }} - RUST_TARGET: ${{ matrix.settings.target }} - GH_TOKEN: ${{ github.token }} - GITHUB_RUN_ID: ${{ github.run_id }} - - - name: Build - run: bun run build - working-directory: packages/desktop - env: - OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ vars.SENTRY_ORG }} - SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }} - SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }} - VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }} - VITE_SENTRY_ENVIRONMENT: ${{ (github.ref_name == 'beta' && 'beta') || 'production' }} - VITE_SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }} - - - name: Package and publish - if: needs.version.outputs.release - run: npx electron-builder ${{ matrix.settings.platform_flag }} --publish always --config electron-builder.config.ts - working-directory: packages/desktop - timeout-minutes: 60 - env: - OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} - GH_TOKEN: ${{ steps.committer.outputs.token }} - CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }} - CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - APPLE_API_KEY: ${{ runner.temp }}/apple-api-key.p8 - APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY }} - APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} - - - name: Package (no publish) - if: ${{ !needs.version.outputs.release }} - run: npx electron-builder ${{ matrix.settings.platform_flag }} --publish never --config electron-builder.config.ts - working-directory: packages/desktop - timeout-minutes: 60 - env: - OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} - - - name: Create and upload macOS .app.tar.gz - if: runner.os == 'macOS' && needs.version.outputs.release - working-directory: packages/desktop/dist - env: - GH_TOKEN: ${{ steps.committer.outputs.token }} - run: | - if [[ "${{ matrix.settings.target }}" == "x86_64-apple-darwin" ]]; then - APP_DIR="mac" - OUT_NAME="opencode-desktop-mac-x64.app.tar.gz" - elif [[ "${{ matrix.settings.target }}" == "aarch64-apple-darwin" ]]; then - APP_DIR="mac-arm64" - OUT_NAME="opencode-desktop-mac-arm64.app.tar.gz" - else - echo "Unknown macOS target: ${{ matrix.settings.target }}" - exit 1 - fi - APP_PATH=$(find "$APP_DIR" -maxdepth 1 -name "*.app" -type d | head -1) - if [ -z "$APP_PATH" ]; then - echo "No .app bundle found in $APP_DIR" - exit 1 - fi - tar -czf "$OUT_NAME" -C "$(dirname "$APP_PATH")" "$(basename "$APP_PATH")" - gh release upload "v${{ needs.version.outputs.version }}" "$OUT_NAME" --clobber --repo "${{ needs.version.outputs.repo }}" - - - name: Verify signed Windows Electron artifacts - if: runner.os == 'Windows' - shell: pwsh - run: | - $files = @() - $files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*.exe" | Select-Object -ExpandProperty FullName - $files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*unpacked\*.exe" | Select-Object -ExpandProperty FullName - $files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*unpacked\resources\opencode-cli.exe" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName - - foreach ($file in $files | Select-Object -Unique) { - $sig = Get-AuthenticodeSignature $file - if ($sig.Status -ne "Valid") { - throw "Invalid signature for ${file}: $($sig.Status)" - } - } - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: opencode-desktop-${{ matrix.settings.target }} - path: packages/desktop/dist/* - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: needs.version.outputs.release - with: - name: latest-yml-${{ matrix.settings.target }} - path: packages/desktop/dist/latest*.yml - - publish: - needs: - - version - - build-cli - - sign-cli-windows - - build-electron - if: always() && !failure() && !cancelled() - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: ./.github/actions/setup-bun - - - name: Login to GitHub Container Registry - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "24" - registry-url: "https://registry.npmjs.org" - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: opencode-cli - path: packages/opencode/dist - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: opencode-cli-windows - path: packages/opencode/dist - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: opencode-cli-signed-windows - path: packages/opencode/dist - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - if: needs.version.outputs.release - with: - pattern: latest-yml-* - path: /tmp/latest-yml - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Cache apt packages (AUR) - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: /var/cache/apt/archives - key: ${{ runner.os }}-apt-aur-${{ hashFiles('.github/workflows/publish.yml') }} - restore-keys: | - ${{ runner.os }}-apt-aur- - - - name: Setup SSH for AUR - run: | - sudo apt-get update - sudo apt-get install -y pacman-package-manager - mkdir -p ~/.ssh - echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" - ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true - - - run: ./script/publish.ts - env: - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - OPENCODE_RELEASE: ${{ needs.version.outputs.release }} - AUR_KEY: ${{ secrets.AUR_KEY }} - GITHUB_TOKEN: ${{ steps.committer.outputs.token }} - GH_REPO: ${{ needs.version.outputs.repo }} - NPM_CONFIG_PROVENANCE: false - LATEST_YML_DIR: /tmp/latest-yml - TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} - TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} diff --git a/reference/opencode-1.15.5/.github/workflows/release-github-action.yml b/reference/opencode-1.15.5/.github/workflows/release-github-action.yml deleted file mode 100755 index 4a1d721..0000000 --- a/reference/opencode-1.15.5/.github/workflows/release-github-action.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: release-github-action - -on: - push: - branches: - - dev - paths: - - "github/**" - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -permissions: - contents: write - -jobs: - release: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 0 - - - run: git fetch --force --tags - - - name: Release - run: | - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" - ./github/script/release diff --git a/reference/opencode-1.15.5/.github/workflows/review.yml b/reference/opencode-1.15.5/.github/workflows/review.yml deleted file mode 100755 index 00a4fba..0000000 --- a/reference/opencode-1.15.5/.github/workflows/review.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: review - -on: - issue_comment: - types: [created] - -jobs: - check-guidelines: - if: | - github.event.issue.pull_request && - startsWith(github.event.comment.body, '/review') && - contains(fromJson('["OWNER","MEMBER"]'), github.event.comment.author_association) - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - pull-requests: write - steps: - - name: Get PR number - id: pr-number - run: | - if [ "${{ github.event_name }}" = "pull_request_target" ]; then - echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT - else - echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT - fi - - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 1 - - - uses: ./.github/actions/setup-bun - - - name: Install opencode - run: curl -fsSL https://opencode.ai/install | bash - - - name: Get PR details - id: pr-details - run: | - gh api /repos/${{ github.repository }}/pulls/${{ steps.pr-number.outputs.number }} > pr_data.json - echo "title=$(jq -r .title pr_data.json)" >> $GITHUB_OUTPUT - echo "sha=$(jq -r .head.sha pr_data.json)" >> $GITHUB_OUTPUT - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check PR guidelines compliance - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: '{ "bash": { "*": "deny", "gh*": "allow", "gh pr review*": "deny" } }' - PR_TITLE: ${{ steps.pr-details.outputs.title }} - run: | - PR_BODY=$(jq -r .body pr_data.json) - opencode run -m opencode/gpt-5.5 --variant medium "A new pull request has been created: '${PR_TITLE}' - - - ${{ steps.pr-number.outputs.number }} - - - - $PR_BODY - - - Please check all the code changes in this pull request against the style guide, also look for any bugs if they exist. Diffs are important but make sure you read the entire file to get proper context. Make it clear the suggestions are merely suggestions and the human can decide what to do - - When critiquing code against the style guide, be sure that the code is ACTUALLY in violation, don't complain about else statements if they already use early returns there. You may complain about excessive nesting though, regardless of else statement usage. - When critiquing code style don't be a zealot, we don't like "let" statements but sometimes they are the simplest option, if someone does a bunch of nesting with let, they should consider using iife (see packages/opencode/src/util.iife.ts) - - Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block. - If you are writing suggested fixes, BE SURE THAT the change you are recommending is actually valid typescript, often I have seen missing closing "}" or other syntax errors. - Generally, write a comment instead of writing suggested change if you can help it. - - Command MUST be like this. - \`\`\` - gh api \ - --method POST \ - -H \"Accept: application/vnd.github+json\" \ - -H \"X-GitHub-Api-Version: 2022-11-28\" \ - /repos/${{ github.repository }}/pulls/${{ steps.pr-number.outputs.number }}/comments \ - -f 'body=[summary of issue]' -f 'commit_id=${{ steps.pr-details.outputs.sha }}' -f 'path=[path-to-file]' -F \"line=[line]\" -f 'side=RIGHT' - \`\`\` - - Only create comments for actual violations. If the code follows all guidelines, comment on the issue using gh cli: 'lgtm' AND NOTHING ELSE!!!!." diff --git a/reference/opencode-1.15.5/.github/workflows/stats.yml b/reference/opencode-1.15.5/.github/workflows/stats.yml deleted file mode 100755 index bc97cfc..0000000 --- a/reference/opencode-1.15.5/.github/workflows/stats.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: stats - -on: - schedule: - - cron: "0 12 * * *" # Run daily at 12:00 UTC - workflow_dispatch: # Allow manual trigger - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -jobs: - stats: - if: github.repository == 'anomalyco/opencode' - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Run stats script - run: bun script/stats.ts - - - name: Commit stats - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add STATS.md - git diff --staged --quiet || git commit -m "ignore: update download stats $(date -I)" - git push - env: - POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }} diff --git a/reference/opencode-1.15.5/.github/workflows/storybook.yml b/reference/opencode-1.15.5/.github/workflows/storybook.yml deleted file mode 100755 index 1e65210..0000000 --- a/reference/opencode-1.15.5/.github/workflows/storybook.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: storybook - -on: - push: - branches: [dev] - paths: - - ".github/workflows/storybook.yml" - - "package.json" - - "bun.lock" - - "packages/storybook/**" - - "packages/ui/**" - pull_request: - branches: [dev] - paths: - - ".github/workflows/storybook.yml" - - "package.json" - - "bun.lock" - - "packages/storybook/**" - - "packages/ui/**" - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - build: - name: storybook build - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Build Storybook - run: bun --cwd packages/storybook build diff --git a/reference/opencode-1.15.5/.github/workflows/sync-zed-extension.yml b/reference/opencode-1.15.5/.github/workflows/sync-zed-extension.yml deleted file mode 100755 index 6e4b440..0000000 --- a/reference/opencode-1.15.5/.github/workflows/sync-zed-extension.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: "sync-zed-extension" - -on: - workflow_dispatch: - release: - types: [published] - -jobs: - zed: - name: Release Zed Extension - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 0 - - - uses: ./.github/actions/setup-bun - - - name: Get version tag - id: get_tag - run: | - if [ "${{ github.event_name }}" = "release" ]; then - TAG="${{ github.event.release.tag_name }}" - else - TAG=$(git tag --list 'v[0-9]*.*' --sort=-version:refname | head -n 1) - fi - echo "tag=${TAG}" >> $GITHUB_OUTPUT - echo "Using tag: ${TAG}" - - - name: Sync Zed extension - run: | - ./script/sync-zed.ts ${{ steps.get_tag.outputs.tag }} - env: - ZED_EXTENSIONS_PAT: ${{ secrets.ZED_EXTENSIONS_PAT }} - ZED_PR_PAT: ${{ secrets.ZED_PR_PAT }} diff --git a/reference/opencode-1.15.5/.github/workflows/test.yml b/reference/opencode-1.15.5/.github/workflows/test.yml deleted file mode 100755 index 4a65b99..0000000 --- a/reference/opencode-1.15.5/.github/workflows/test.yml +++ /dev/null @@ -1,166 +0,0 @@ -name: test - -on: - push: - branches: - - dev - pull_request: - workflow_dispatch: - -concurrency: - # Keep every run on dev so cancelled checks do not pollute the default branch - # commit history. PRs and other branches still share a group and cancel stale runs. - group: ${{ case(github.ref == 'refs/heads/dev', format('{0}-{1}', github.workflow, github.run_id), format('{0}-{1}', github.workflow, github.event.pull_request.number || github.ref)) }} - cancel-in-progress: true - -permissions: - contents: read - checks: write - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - unit: - name: unit (${{ matrix.settings.name }}) - strategy: - fail-fast: false - matrix: - settings: - - name: linux - host: blacksmith-4vcpu-ubuntu-2404 - - name: windows - host: blacksmith-4vcpu-windows-2025 - runs-on: ${{ matrix.settings.host }} - defaults: - run: - shell: bash - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "24" - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Configure git identity - run: | - git config --global user.email "bot@opencode.ai" - git config --global user.name "opencode" - - - name: Cache Turbo - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: node_modules/.cache/turbo - key: turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json') }}-${{ github.sha }} - restore-keys: | - turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json') }}- - turbo-${{ runner.os }}- - - - name: Run unit tests - run: bun turbo test:ci - env: - OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER: ${{ runner.os == 'Windows' && 'true' || 'false' }} - - - name: Run HttpApi exerciser gates - if: runner.os == 'Linux' - working-directory: packages/opencode - run: bun run test:httpapi - - - name: Publish unit reports - if: always() - uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v6.4.0 - with: - report_paths: packages/*/.artifacts/unit/junit.xml - check_name: "unit results (${{ matrix.settings.name }})" - detailed_summary: true - include_time_in_summary: true - fail_on_failure: false - - - name: Upload unit artifacts - if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: unit-${{ matrix.settings.name }}-${{ github.run_attempt }} - include-hidden-files: true - if-no-files-found: ignore - retention-days: 7 - path: packages/*/.artifacts/unit/junit.xml - - e2e: - name: e2e (${{ matrix.settings.name }}) - strategy: - fail-fast: false - matrix: - settings: - - name: linux - host: blacksmith-4vcpu-ubuntu-2404 - - name: windows - host: blacksmith-4vcpu-windows-2025 - runs-on: ${{ matrix.settings.host }} - env: - PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers - defaults: - run: - shell: bash - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "24" - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Read Playwright version - id: playwright-version - run: | - version=$(node -e 'console.log(require("./package.json").workspaces.catalog["@playwright/test"])') - echo "version=$version" >> "$GITHUB_OUTPUT" - - - name: Cache Playwright browsers - id: playwright-cache - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: ${{ github.workspace }}/.playwright-browsers - key: ${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright-version.outputs.version }}-chromium - - - name: Install Playwright system dependencies - if: runner.os == 'Linux' - working-directory: packages/app - run: bunx playwright install-deps chromium - - - name: Install Playwright browsers - if: steps.playwright-cache.outputs.cache-hit != 'true' - working-directory: packages/app - run: bunx playwright install chromium - - - name: Run app e2e tests - run: bun --cwd packages/app test:e2e:local - env: - CI: true - PLAYWRIGHT_JUNIT_OUTPUT: e2e/junit-${{ matrix.settings.name }}.xml - timeout-minutes: 30 - - - name: Upload Playwright artifacts - if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: playwright-${{ matrix.settings.name }}-${{ github.run_attempt }} - if-no-files-found: ignore - retention-days: 7 - path: | - packages/app/e2e/junit-*.xml - packages/app/e2e/test-results - packages/app/e2e/playwright-report diff --git a/reference/opencode-1.15.5/.github/workflows/triage.yml b/reference/opencode-1.15.5/.github/workflows/triage.yml deleted file mode 100755 index 27852a1..0000000 --- a/reference/opencode-1.15.5/.github/workflows/triage.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: triage - -on: - issues: - types: [opened] - -jobs: - triage: - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - issues: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 1 - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Install opencode - run: curl -fsSL https://opencode.ai/install | bash - - - name: Triage issue - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - ISSUE_TITLE: ${{ github.event.issue.title }} - ISSUE_BODY: ${{ github.event.issue.body }} - run: | - opencode run --agent triage "The following issue was just opened, triage it: - - Title: $ISSUE_TITLE - - $ISSUE_BODY" diff --git a/reference/opencode-1.15.5/.github/workflows/typecheck.yml b/reference/opencode-1.15.5/.github/workflows/typecheck.yml deleted file mode 100755 index fc9a527..0000000 --- a/reference/opencode-1.15.5/.github/workflows/typecheck.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: typecheck - -on: - push: - branches: [dev] - pull_request: - branches: [dev] - workflow_dispatch: - -jobs: - typecheck: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Run typecheck - run: bun typecheck diff --git a/reference/opencode-1.15.5/.gitignore b/reference/opencode-1.15.5/.gitignore deleted file mode 100755 index 19198a7..0000000 --- a/reference/opencode-1.15.5/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -.DS_Store -node_modules -.worktrees -.sst -.env -.env.local -.idea -.vscode -.codex -*~ -playground -tmp -dist -ts-dist -.turbo -**/.serena -.serena/ -/result -refs -Session.vim -/opencode.json -a.out -target -.scripts -.direnv/ - -# Local dev files -opencode-dev -UPCOMING_CHANGELOG.md -logs/ -*.bun-build -tsconfig.tsbuildinfo diff --git a/reference/opencode-1.15.5/.gitkeep b/reference/opencode-1.15.5/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/reference/opencode-1.15.5/.gitleaksignore b/reference/opencode-1.15.5/.gitleaksignore deleted file mode 100755 index cc01a28..0000000 --- a/reference/opencode-1.15.5/.gitleaksignore +++ /dev/null @@ -1,5 +0,0 @@ -# Fake secret-looking strings used by HTTP recorder redaction tests. -afa57acfda894e0ebf3c637dd710310b705c0a2f:packages/http-recorder/test/record-replay.test.ts:generic-api-key:69 -afa57acfda894e0ebf3c637dd710310b705c0a2f:packages/http-recorder/test/record-replay.test.ts:generic-api-key:92 -afa57acfda894e0ebf3c637dd710310b705c0a2f:packages/http-recorder/test/record-replay.test.ts:generic-api-key:146 -afa57acfda894e0ebf3c637dd710310b705c0a2f:packages/http-recorder/test/record-replay.test.ts:gcp-api-key:71 diff --git a/reference/opencode-1.15.5/.husky/pre-push b/reference/opencode-1.15.5/.husky/pre-push deleted file mode 100755 index 5d3cc53..0000000 --- a/reference/opencode-1.15.5/.husky/pre-push +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -set -e -# Check if bun version matches package.json -# keep in sync with packages/script/src/index.ts semver qualifier -bun -e ' -import { semver } from "bun"; -const pkg = await Bun.file("package.json").json(); -const expectedBunVersion = pkg.packageManager?.split("@")[1]; -if (!expectedBunVersion) { - throw new Error("packageManager field not found in root package.json"); -} -const expectedBunVersionRange = `^${expectedBunVersion}`; -if (!semver.satisfies(process.versions.bun, expectedBunVersionRange)) { - throw new Error(`This script requires bun@${expectedBunVersionRange}, but you are using bun@${process.versions.bun}`); -} -if (process.versions.bun !== expectedBunVersion) { - console.warn(`Warning: Bun version ${process.versions.bun} differs from expected ${expectedBunVersion}`); -} -' -bun typecheck diff --git a/reference/opencode-1.15.5/.opencode/agent/duplicate-pr.md b/reference/opencode-1.15.5/.opencode/agent/duplicate-pr.md deleted file mode 100755 index c9c932e..0000000 --- a/reference/opencode-1.15.5/.opencode/agent/duplicate-pr.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -mode: primary -hidden: true -model: opencode/claude-haiku-4-5 -color: "#E67E22" -tools: - "*": false - "github-pr-search": true ---- - -You are a duplicate PR detection agent. When a PR is opened, your job is to search for potentially duplicate or related open PRs. - -Use the github-pr-search tool to search for PRs that might be addressing the same issue or feature. - -IMPORTANT: The input will contain a line `CURRENT_PR_NUMBER: NNNN`. This is the current PR number, you should not mark that the current PR as a duplicate of itself. - -Search using keywords from the PR title and description. Try multiple searches with different relevant terms. - -If you find potential duplicates: - -- List them with their titles and URLs -- Briefly explain why they might be related - -If no duplicates are found, say so clearly. BUT ONLY SAY "No duplicate PRs found" (don't say anything else if no dups) - -Keep your response concise and actionable. diff --git a/reference/opencode-1.15.5/.opencode/agent/triage.md b/reference/opencode-1.15.5/.opencode/agent/triage.md deleted file mode 100755 index 03df339..0000000 --- a/reference/opencode-1.15.5/.opencode/agent/triage.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -mode: primary -hidden: true -model: opencode/gpt-5.4-nano -color: "#44BA81" -tools: - "*": false - "github-triage": true ---- - -You are a triage agent responsible for triaging github issues. - -Use your github-triage tool to triage issues. - -This file is the source of truth for ownership/routing rules. - -Assign issues by choosing the team with the strongest overlap. The github-triage tool will assign a random member from that team. - -Do not add labels to issues. Only assign an owner. - -When calling github-triage, pass one of these team values: tui, desktop_web, core, inference, windows. - -## Teams - -### TUI - -Terminal UI issues, including rendering, keybindings, scrolling, terminal compatibility, SSH behavior, crashes in the TUI, and low-level TUI performance. - -### Desktop / Web - -Desktop application and browser-based app issues, including `opencode web`, desktop-specific UI behavior, packaging, and web view problems. - -### Core - -Core opencode server and harness issues, including sqlite, snapshots, memory, API behavior, agent context construction, tool execution, provider integrations, model behavior, documentation, and larger architectural features. - -### Inference - -OpenCode Zen, OpenCode Go, and billing issues. - -### Windows - -Windows-specific issues, including native Windows behavior, WSL interactions, path handling, shell compatibility, and installation or runtime problems that only happen on Windows. diff --git a/reference/opencode-1.15.5/.opencode/command/ai-deps.md b/reference/opencode-1.15.5/.opencode/command/ai-deps.md deleted file mode 100755 index 83783d5..0000000 --- a/reference/opencode-1.15.5/.opencode/command/ai-deps.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -description: "Bump AI sdk dependencies minor / patch versions only" ---- - -Please read @package.json and @packages/opencode/package.json. - -Your job is to look into AI SDK dependencies, figure out if they have versions that can be upgraded (minor or patch versions ONLY no major ignore major changes). - -I want a report of every dependency and the version that can be upgraded to. -What would be even better is if you can give me brief summary of the changes for each dep and a link to the changelog for each dependency, or at least some reference info so I can see what bugs were fixed or new features were added. - -Consider using subagents for each dep to save your context window. - -Here is a short list of some deps (please be comprehensive tho): - -- "ai" -- "@ai-sdk/openai" -- "@ai-sdk/anthropic" -- "@openrouter/ai-sdk-provider" -- etc, etc - -DO NOT upgrade the dependencies yet, just make a list of all dependencies and their versions that can be upgraded to minor or patch versions only. - -Write up your findings to ai-sdk-updates.md diff --git a/reference/opencode-1.15.5/.opencode/command/changelog.md b/reference/opencode-1.15.5/.opencode/command/changelog.md deleted file mode 100755 index b28d963..0000000 --- a/reference/opencode-1.15.5/.opencode/command/changelog.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -model: opencode/gpt-5.4 ---- - -Create `UPCOMING_CHANGELOG.md` from the structured changelog input below. -If `UPCOMING_CHANGELOG.md` already exists, ignore its current contents completely. -Do not preserve, merge, or reuse text from the existing file. - -The input already contains the exact commit range since the last non-draft release. -The commits are already filtered to the release-relevant packages and grouped into -the release sections. Do not fetch GitHub releases, PRs, or build your own commit list. -The input may also include a `## Community Contributors Input` section. - -Before writing any entry you keep, inspect the real diff with -`git show --stat --format='' ` or `git show --format='' ` so you can -understand the actual code changes and not just the commit message (they may be misleading). -Do not use `git log` or author metadata when deciding attribution. - -Rules: - -- Write the final file with release sections in this order: - `## Core`, `## TUI`, `## Desktop`, `## SDK`, `## Extensions` -- Only include sections that have at least one notable entry -- Within each release section, keep bug fixes grouped under `### Bugfixes` -- Keep other notable entries under `### Improvements` when a section has bug fixes too -- Omit empty subsections -- Keep one bullet per commit you keep -- Skip commits that are entirely internal, CI, tests, refactors, or otherwise not user-facing -- Start each bullet with a capital letter -- Prefer what changed for users over what code changed internally -- Do not copy raw commit prefixes like `fix:` or `feat:` or trailing PR numbers like `(#123)` -- Community attribution is deterministic: only preserve an existing `(@username)` suffix from the changelog input -- If an input bullet has no `(@username)` suffix, do not add one -- Never add a new `(@username)` suffix from `git show`, commit authors, names, or email addresses -- If no notable entries remain and there is no contributor block, write exactly `No notable changes.` -- If no notable entries remain but there is a contributor block, omit all release sections and return only the contributor block -- If the input contains `## Community Contributors Input`, append the block below that heading to the end of the final file verbatim -- Do not add, remove, rewrite, or reorder contributor names or commit titles in that block -- Do not derive the thank-you section from the main summary bullets -- Do not include the heading `## Community Contributors Input` in the final file -- Focus on writing the least words to get your point across - users will skim read the changelog, so we should be precise - -**Importantly, the changelog is for users (who are at least slightly technical), they may use the TUI, Desktop, SDK, Plugins and so forth. Be thorough in understanding flow on effects may not be immediately apparent. e.g. a package upgrade looks internal but may patch a bug. Or a refactor may also stabilise some race condition that fixes bugs for users. The PR title/body + commit message will give you the authors context, usually containing the outcome not just technical detail** - - - -!`bun script/raw-changelog.ts $ARGUMENTS` - - diff --git a/reference/opencode-1.15.5/.opencode/command/commit.md b/reference/opencode-1.15.5/.opencode/command/commit.md deleted file mode 100755 index e88932a..0000000 --- a/reference/opencode-1.15.5/.opencode/command/commit.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -description: git commit and push -model: opencode/kimi-k2.5 -subtask: true ---- - -commit and push - -make sure it includes a prefix like -docs: -tui: -core: -ci: -ignore: -wip: - -For anything in the packages/web use the docs: prefix. - -prefer to explain WHY something was done from an end user perspective instead of -WHAT was done. - -do not do generic messages like "improved agent experience" be very specific -about what user facing changes were made - -if there are conflicts DO NOT FIX THEM. notify me and I will fix them - -## GIT DIFF - -!`git diff` - -## GIT DIFF --cached - -!`git diff --cached` - -## GIT STATUS --short - -!`git status --short` diff --git a/reference/opencode-1.15.5/.opencode/command/issues.md b/reference/opencode-1.15.5/.opencode/command/issues.md deleted file mode 100755 index 75b5961..0000000 --- a/reference/opencode-1.15.5/.opencode/command/issues.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: "find issue(s) on github" -model: opencode/claude-haiku-4-5 ---- - -Search through existing issues in anomalyco/opencode using the gh cli to find issues matching this query: - -$ARGUMENTS - -Consider: - -1. Similar titles or descriptions -2. Same error messages or symptoms -3. Related functionality or components -4. Similar feature requests - -Please list any matching issues with: - -- Issue number and title -- Brief explanation of why it matches the query -- Link to the issue - -If no clear matches are found, say so. diff --git a/reference/opencode-1.15.5/.opencode/command/learn.md b/reference/opencode-1.15.5/.opencode/command/learn.md deleted file mode 100755 index fe4965a..0000000 --- a/reference/opencode-1.15.5/.opencode/command/learn.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -description: Extract non-obvious learnings from session to AGENTS.md files to build codebase understanding ---- - -Analyze this session and extract non-obvious learnings to add to AGENTS.md files. - -AGENTS.md files can exist at any directory level, not just the project root. When an agent reads a file, any AGENTS.md in parent directories are automatically loaded into the context of the tool read. Place learnings as close to the relevant code as possible: - -- Project-wide learnings → root AGENTS.md -- Package/module-specific → packages/foo/AGENTS.md -- Feature-specific → src/auth/AGENTS.md - -What counts as a learning (non-obvious discoveries only): - -- Hidden relationships between files or modules -- Execution paths that differ from how code appears -- Non-obvious configuration, env vars, or flags -- Debugging breakthroughs when error messages were misleading -- API/tool quirks and workarounds -- Build/test commands not in README -- Architectural decisions and constraints -- Files that must change together - -What NOT to include: - -- Obvious facts from documentation -- Standard language/framework behavior -- Things already in an AGENTS.md -- Verbose explanations -- Session-specific details - -Process: - -1. Review session for discoveries, errors that took multiple attempts, unexpected connections -2. Determine scope - what directory does each learning apply to? -3. Read existing AGENTS.md files at relevant levels -4. Create or update AGENTS.md at the appropriate level -5. Keep entries to 1-3 lines per insight - -After updating, summarize which AGENTS.md files were created/updated and how many learnings per file. - -$ARGUMENTS diff --git a/reference/opencode-1.15.5/.opencode/command/rmslop.md b/reference/opencode-1.15.5/.opencode/command/rmslop.md deleted file mode 100755 index 02c9fc0..0000000 --- a/reference/opencode-1.15.5/.opencode/command/rmslop.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: Remove AI code slop ---- - -Check the diff against dev, and remove all AI generated slop introduced in this branch. - -This includes: - -- Extra comments that a human wouldn't add or is inconsistent with the rest of the file -- Extra defensive checks or try/catch blocks that are abnormal for that area of the codebase (especially if called by trusted / validated codepaths) -- Casts to any to get around type issues -- Any other style that is inconsistent with the file -- Unnecessary emoji usage - -Report at the end with only a 1-3 sentence summary of what you changed diff --git a/reference/opencode-1.15.5/.opencode/command/spellcheck.md b/reference/opencode-1.15.5/.opencode/command/spellcheck.md deleted file mode 100755 index 0abf23c..0000000 --- a/reference/opencode-1.15.5/.opencode/command/spellcheck.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -description: spellcheck all markdown file changes ---- - -Look at all the unstaged changes to markdown (.md, .mdx) files, pull out the lines that have changed, and check for spelling and grammar errors. diff --git a/reference/opencode-1.15.5/.opencode/command/translate.md b/reference/opencode-1.15.5/.opencode/command/translate.md deleted file mode 100755 index ed185b1..0000000 --- a/reference/opencode-1.15.5/.opencode/command/translate.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -description: translate English to other languages -model: opencode/claude-opus-4-7 ---- - -run git diff and translate changed english doc and UI copy files to other international languages. Translate all languages in parallel to save time. - -Requirements: - -- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure). -- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks. -- Also preserve every term listed in the Do-Not-Translate glossary below. -- Also apply locale-specific guidance from `.opencode/glossary/.md` when available (for example, `zh-cn.md`). -- Do not modify fenced code blocks. diff --git a/reference/opencode-1.15.5/.opencode/env.d.ts b/reference/opencode-1.15.5/.opencode/env.d.ts deleted file mode 100755 index f2b13a9..0000000 --- a/reference/opencode-1.15.5/.opencode/env.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module "*.txt" { - const content: string - export default content -} diff --git a/reference/opencode-1.15.5/.opencode/glossary/README.md b/reference/opencode-1.15.5/.opencode/glossary/README.md deleted file mode 100755 index 9839003..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Locale Glossaries - -Use this folder for locale-specific translation guidance that supplements `.opencode/agent/translator.md`. - -The global glossary in `translator.md` remains the source of truth for shared do-not-translate terms (commands, code, paths, product names, etc.). These locale files capture community learnings about phrasing and terminology preferences. - -## File Naming - -- One file per locale -- Use lowercase locale slugs that match docs locales when possible (for example, `zh-cn.md`, `zh-tw.md`) -- If only language-level guidance exists, use the language code (for example, `fr.md`) -- Some repo locale slugs may be aliases/non-BCP47 for consistency (for example, `br` for Brazilian Portuguese / `pt-BR`) - -## What To Put In A Locale File - -- **Sources**: PRs/issues/discussions that motivated the guidance -- **Do Not Translate (Locale Additions)**: locale-specific terms or casing decisions -- **Preferred Terms**: recurring UI/docs words with preferred translations -- **Guidance**: tone, style, and consistency notes -- **Avoid** (optional): common literal translations or wording we should avoid -- If the repo uses a locale alias slug, document the alias in **Guidance** (for example, prose may mention `pt-BR` while config/examples use `br`) - -Prefer guidance that is: - -- Repeated across multiple docs/screens -- Easy to apply consistently -- Backed by a community contribution or review discussion - -## Template - -```md -# Glossary - -## Sources - -- PR #12345: https://github.com/anomalyco/opencode/pull/12345 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing) - -## Preferred Terms - -| English | Preferred | Notes | -| ------- | --------- | --------- | -| prompt | ... | preferred | -| session | ... | preferred | - -## Guidance - -- Prefer natural phrasing over literal translation - -## Avoid - -- Avoid ... when ... -``` - -## Contribution Notes - -- Mark entries as preferred when they may evolve -- Keep examples short -- Add or update the `Sources` section whenever you add a new rule -- Prefer PR-backed guidance over invented term mappings; start with general guidance if no term-level corrections exist yet diff --git a/reference/opencode-1.15.5/.opencode/glossary/ar.md b/reference/opencode-1.15.5/.opencode/glossary/ar.md deleted file mode 100755 index 3735552..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/ar.md +++ /dev/null @@ -1,28 +0,0 @@ -# ar Glossary - -## Sources - -- PR #9947: https://github.com/anomalyco/opencode/pull/9947 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Arabic phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- For RTL text, treat code, commands, and paths as LTR artifacts and keep their character order unchanged - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Arabic terms for the same recurring UI action once a preferred term is established diff --git a/reference/opencode-1.15.5/.opencode/glossary/br.md b/reference/opencode-1.15.5/.opencode/glossary/br.md deleted file mode 100755 index fd3e725..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/br.md +++ /dev/null @@ -1,34 +0,0 @@ -# br Glossary - -## Sources - -- PR #10086: https://github.com/anomalyco/opencode/pull/10086 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Locale code `br` in repo config, code, and paths (repo alias for Brazilian Portuguese) - -## Preferred Terms - -These are PR-backed locale naming preferences and may evolve. - -| English / Context | Preferred | Notes | -| ---------------------------------------- | ------------------------------ | ------------------------------------------------------------- | -| Brazilian Portuguese (prose locale name) | `pt-BR` | Use standard locale naming in prose when helpful | -| Repo locale slug (code/config) | `br` | PR #10086 uses `br` for consistency/simplicity | -| Browser locale detection | `pt`, `pt-br`, `pt-BR` -> `br` | Preserve this mapping in docs/examples about locale detection | - -## Guidance - -- This file covers Brazilian Portuguese (`pt-BR`), but the repo locale code is `br` -- Use natural Brazilian Portuguese phrasing over literal translation -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- Keep repo locale identifiers as implemented in code/config (`br`) even when prose mentions `pt-BR` - -## Avoid - -- Avoid changing repo locale code references from `br` to `pt-br` in code snippets, paths, or config examples -- Avoid mixing Portuguese variants when a Brazilian Portuguese form is established diff --git a/reference/opencode-1.15.5/.opencode/glossary/bs.md b/reference/opencode-1.15.5/.opencode/glossary/bs.md deleted file mode 100755 index aa3bd96..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/bs.md +++ /dev/null @@ -1,33 +0,0 @@ -# bs Glossary - -## Sources - -- PR #12283: https://github.com/anomalyco/opencode/pull/12283 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -These are PR-backed locale naming preferences and may evolve. - -| English / Context | Preferred | Notes | -| ---------------------------------- | ---------- | ------------------------------------------------- | -| Bosnian language label (UI) | `Bosanski` | PR #12283 tested switching language to `Bosanski` | -| Repo locale slug (code/config) | `bs` | Preserve in code, config, paths, and examples | -| Browser locale detection (Bosnian) | `bs` | PR #12283 added `bs` locale auto-detection | - -## Guidance - -- Use natural Bosnian phrasing over literal translation -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- Keep repo locale references as `bs` in code/config, and use `Bosanski` for the user-facing language name when applicable - -## Avoid - -- Avoid changing repo locale references from `bs` to another slug in code snippets or config examples -- Avoid translating product and protocol names that are fixed identifiers diff --git a/reference/opencode-1.15.5/.opencode/glossary/da.md b/reference/opencode-1.15.5/.opencode/glossary/da.md deleted file mode 100755 index e632221..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/da.md +++ /dev/null @@ -1,27 +0,0 @@ -# da Glossary - -## Sources - -- PR #9821: https://github.com/anomalyco/opencode/pull/9821 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Danish phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Danish terms for the same recurring UI action once a preferred term is established diff --git a/reference/opencode-1.15.5/.opencode/glossary/de.md b/reference/opencode-1.15.5/.opencode/glossary/de.md deleted file mode 100755 index 0d2c49f..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/de.md +++ /dev/null @@ -1,27 +0,0 @@ -# de Glossary - -## Sources - -- PR #9817: https://github.com/anomalyco/opencode/pull/9817 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural German phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple German terms for the same recurring UI action once a preferred term is established diff --git a/reference/opencode-1.15.5/.opencode/glossary/es.md b/reference/opencode-1.15.5/.opencode/glossary/es.md deleted file mode 100755 index dc9b977..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/es.md +++ /dev/null @@ -1,27 +0,0 @@ -# es Glossary - -## Sources - -- PR #9817: https://github.com/anomalyco/opencode/pull/9817 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Spanish phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Spanish terms for the same recurring UI action once a preferred term is established diff --git a/reference/opencode-1.15.5/.opencode/glossary/fr.md b/reference/opencode-1.15.5/.opencode/glossary/fr.md deleted file mode 100755 index 074c4de..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/fr.md +++ /dev/null @@ -1,27 +0,0 @@ -# fr Glossary - -## Sources - -- PR #9821: https://github.com/anomalyco/opencode/pull/9821 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural French phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple French terms for the same recurring UI action once a preferred term is established diff --git a/reference/opencode-1.15.5/.opencode/glossary/ja.md b/reference/opencode-1.15.5/.opencode/glossary/ja.md deleted file mode 100755 index f0159ca..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/ja.md +++ /dev/null @@ -1,33 +0,0 @@ -# ja Glossary - -## Sources - -- PR #9821: https://github.com/anomalyco/opencode/pull/9821 -- PR #13160: https://github.com/anomalyco/opencode/pull/13160 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -These are PR-backed wording preferences and may evolve. - -| English / Context | Preferred | Notes | -| --------------------------- | ----------------------- | ------------------------------------- | -| WSL integration (UI label) | `WSL連携` | PR #13160 prefers this over `WSL統合` | -| WSL integration description | `WindowsのWSL環境で...` | PR #13160 improved phrasing naturally | - -## Guidance - -- Prefer natural Japanese phrasing over literal translation -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- In WSL integration text, follow PR #13160 wording direction for more natural Japanese phrasing - -## Avoid - -- Avoid `WSL統合` in the WSL integration UI context where `WSL連携` is the reviewed wording -- Avoid translating product and protocol names that are fixed identifiers diff --git a/reference/opencode-1.15.5/.opencode/glossary/ko.md b/reference/opencode-1.15.5/.opencode/glossary/ko.md deleted file mode 100755 index 71385c8..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/ko.md +++ /dev/null @@ -1,27 +0,0 @@ -# ko Glossary - -## Sources - -- PR #9817: https://github.com/anomalyco/opencode/pull/9817 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Korean phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Korean terms for the same recurring UI action once a preferred term is established diff --git a/reference/opencode-1.15.5/.opencode/glossary/no.md b/reference/opencode-1.15.5/.opencode/glossary/no.md deleted file mode 100755 index d7159dc..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/no.md +++ /dev/null @@ -1,38 +0,0 @@ -# no Glossary - -## Sources - -- PR #10018: https://github.com/anomalyco/opencode/pull/10018 -- PR #12935: https://github.com/anomalyco/opencode/pull/12935 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Sound names (PR #10018 notes these were intentionally left untranslated) - -## Preferred Terms - -These are PR-backed corrections and may evolve. - -| English / Context | Preferred | Notes | -| ----------------------------------- | ------------ | ----------------------------- | -| Save (data persistence action) | `Lagre` | Prefer over `Spare` | -| Disabled (feature/state) | `deaktivert` | Prefer over `funksjonshemmet` | -| API keys | `API Nøkler` | Prefer over `API Taster` | -| Cost (noun) | `Kostnad` | Prefer over verb form `Koste` | -| Show/View (imperative button label) | `Vis` | Prefer over `Utsikt` | - -## Guidance - -- Prefer natural Norwegian Bokmal (Bokmål) wording over literal translation -- Keep tone clear and practical in UI labels -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- Keep recurring UI terms consistent once a preferred term is chosen - -## Avoid - -- Avoid `Spare` for save actions in persistence contexts -- Avoid `funksjonshemmet` for disabled feature states -- Avoid `API Taster`, `Koste`, and `Utsikt` in the corrected contexts above diff --git a/reference/opencode-1.15.5/.opencode/glossary/pl.md b/reference/opencode-1.15.5/.opencode/glossary/pl.md deleted file mode 100755 index e9bad7a..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/pl.md +++ /dev/null @@ -1,27 +0,0 @@ -# pl Glossary - -## Sources - -- PR #9884: https://github.com/anomalyco/opencode/pull/9884 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Polish phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Polish terms for the same recurring UI action once a preferred term is established diff --git a/reference/opencode-1.15.5/.opencode/glossary/ru.md b/reference/opencode-1.15.5/.opencode/glossary/ru.md deleted file mode 100755 index 6fee0f9..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/ru.md +++ /dev/null @@ -1,27 +0,0 @@ -# ru Glossary - -## Sources - -- PR #9882: https://github.com/anomalyco/opencode/pull/9882 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -No PR-backed term mappings yet. Add entries here when review PRs introduce repeated wording corrections. - -## Guidance - -- Prefer natural Russian phrasing over literal translation -- Keep tone clear and direct in UI labels and docs prose -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths - -## Avoid - -- Avoid translating product and protocol names that are fixed identifiers -- Avoid mixing multiple Russian terms for the same recurring UI action once a preferred term is established diff --git a/reference/opencode-1.15.5/.opencode/glossary/th.md b/reference/opencode-1.15.5/.opencode/glossary/th.md deleted file mode 100755 index 7b5a31d..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/th.md +++ /dev/null @@ -1,34 +0,0 @@ -# th Glossary - -## Sources - -- PR #10809: https://github.com/anomalyco/opencode/pull/10809 -- PR #11496: https://github.com/anomalyco/opencode/pull/11496 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only in commands, package names, paths, or code) -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -These are PR-backed preferences and may evolve. - -| English / Context | Preferred | Notes | -| ------------------------------------- | --------------------- | -------------------------------------------------------------------------------- | -| Thai language label in language lists | `ไทย` | PR #10809 standardized this across locales | -| Language names in language pickers | Native names (static) | PR #11496: keep names like `English`, `Deutsch`, `ไทย` consistent across locales | - -## Guidance - -- Prefer natural Thai phrasing over literal translation -- Keep tone short and clear for buttons and labels -- Preserve technical artifacts exactly: commands, flags, code, URLs, model IDs, and file paths -- Keep language names static/native in language pickers instead of translating them per current locale (PR #11496) - -## Avoid - -- Avoid translating language names differently per current locale in language lists -- Avoid changing `ไทย` to another display form for the Thai language option unless the product standard changes diff --git a/reference/opencode-1.15.5/.opencode/glossary/tr.md b/reference/opencode-1.15.5/.opencode/glossary/tr.md deleted file mode 100755 index 72b1cdf..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/tr.md +++ /dev/null @@ -1,38 +0,0 @@ -# tr Glossary - -## Sources - -- PR #15835: https://github.com/anomalyco/opencode/pull/15835 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose, docs, and UI copy) -- Keep lowercase `opencode` in commands, package names, paths, URLs, and other exact identifiers -- `` stays the literal key token in code blocks; use `Tab` for the nearby explanatory label in prose -- Commands, flags, file paths, and code literals (keep exactly as written) - -## Preferred Terms - -These are PR-backed wording preferences and may evolve. - -| English / Context | Preferred | Notes | -| ------------------------- | --------------------------------------- | ------------------------------------------------------------- | -| available in beta | `beta olarak mevcut` | Prefer this over `beta olarak kullanılabilir` | -| privacy-first | `Gizlilik öncelikli tasarlandı` | Prefer this over `Önce gizlilik için tasarlandı` | -| connect your local models | `yerel modellerinizi bağlayabilirsiniz` | Use the fuller, more direct action phrase | -| `` key label | `Tab` | Use `Tab` in prose; keep `` in literal UI or code blocks | -| cross-platform | `cross-platform (tüm platformlarda)` | Keep the English term, add a short clarification when helpful | - -## Guidance - -- Prefer natural Turkish phrasing over literal translation -- Merge broken sentence fragments into one clear sentence when the source is a single thought -- Keep product naming consistent: `OpenCode` in prose, `opencode` only for exact technical identifiers -- When an English technical term is intentionally kept, add a short Turkish clarification only if it improves readability - -## Avoid - -- Avoid `beta olarak kullanılabilir` when `beta olarak mevcut` fits -- Avoid `Önce gizlilik için tasarlandı`; use the more natural reviewed wording instead -- Avoid `Sekme` for the translated key label in prose when referring to `` -- Avoid changing `opencode` to `OpenCode` inside commands, URLs, package names, or code literals diff --git a/reference/opencode-1.15.5/.opencode/glossary/zh-cn.md b/reference/opencode-1.15.5/.opencode/glossary/zh-cn.md deleted file mode 100755 index 054e94b..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/zh-cn.md +++ /dev/null @@ -1,42 +0,0 @@ -# zh-cn Glossary - -## Sources - -- PR #13942: https://github.com/anomalyco/opencode/pull/13942 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only when it is part of commands, package names, paths, or code) -- `OpenCode Zen` -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- `Model Context Protocol` (prefer the English expansion when introducing `MCP`) - -## Preferred Terms - -These are preferred terms for docs/UI prose and may evolve. - -| English | Preferred | Notes | -| ----------------------- | --------- | ------------------------------------------- | -| prompt | 提示词 | Keep `--prompt` unchanged in flags/code | -| session | 会话 | | -| provider | 提供商 | | -| share link / shared URL | 分享链接 | Prefer `分享` for user-facing share actions | -| headless (server) | 无界面 | Docs wording | -| authentication | 认证 | Prefer in auth/OAuth contexts | -| cache | 缓存 | | -| keybind / shortcut | 快捷键 | User-facing docs wording | -| workflow | 工作流 | e.g. GitHub Actions workflow | - -## Guidance - -- Prefer natural, concise phrasing over literal translation -- Keep the tone direct and friendly (PR #13942 consistently moved wording in this direction) -- Preserve technical artifacts exactly: commands, flags, code, inline code, URLs, file paths, model IDs -- Keep enum-like values in English when they are literals (for example, `default`, `json`) -- Prefer consistent terminology across pages once a term is chosen (`会话`, `提供商`, `提示词`, etc.) - -## Avoid - -- Avoid `opencode` in prose when referring to the product name; use `OpenCode` -- Avoid mixing alternative terms for the same concept across docs when a preferred term is already established diff --git a/reference/opencode-1.15.5/.opencode/glossary/zh-tw.md b/reference/opencode-1.15.5/.opencode/glossary/zh-tw.md deleted file mode 100755 index 283660e..0000000 --- a/reference/opencode-1.15.5/.opencode/glossary/zh-tw.md +++ /dev/null @@ -1,42 +0,0 @@ -# zh-tw Glossary - -## Sources - -- PR #13942: https://github.com/anomalyco/opencode/pull/13942 - -## Do Not Translate (Locale Additions) - -- `OpenCode` (preserve casing in prose; keep `opencode` only when it is part of commands, package names, paths, or code) -- `OpenCode Zen` -- `OpenCode CLI` -- `CLI`, `TUI`, `MCP`, `OAuth` -- `Model Context Protocol` (prefer the English expansion when introducing `MCP`) - -## Preferred Terms - -These are preferred terms for docs/UI prose and may evolve. - -| English | Preferred | Notes | -| ----------------------- | --------- | ------------------------------------------- | -| prompt | 提示詞 | Keep `--prompt` unchanged in flags/code | -| session | 工作階段 | | -| provider | 供應商 | | -| share link / shared URL | 分享連結 | Prefer `分享` for user-facing share actions | -| headless (server) | 無介面 | Docs wording | -| authentication | 認證 | Prefer in auth/OAuth contexts | -| cache | 快取 | | -| keybind / shortcut | 快捷鍵 | User-facing docs wording | -| workflow | 工作流程 | e.g. GitHub Actions workflow | - -## Guidance - -- Prefer natural, concise phrasing over literal translation -- Keep the tone direct and friendly (PR #13942 consistently moved wording in this direction) -- Preserve technical artifacts exactly: commands, flags, code, inline code, URLs, file paths, model IDs -- Keep enum-like values in English when they are literals (for example, `default`, `json`) -- Prefer consistent terminology across pages once a term is chosen (`工作階段`, `供應商`, `提示詞`, etc.) - -## Avoid - -- Avoid `opencode` in prose when referring to the product name; use `OpenCode` -- Avoid mixing alternative terms for the same concept across docs when a preferred term is already established diff --git a/reference/opencode-1.15.5/.opencode/opencode.jsonc b/reference/opencode-1.15.5/.opencode/opencode.jsonc deleted file mode 100755 index 0ae2fbe..0000000 --- a/reference/opencode-1.15.5/.opencode/opencode.jsonc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "$schema": "https://opencode.ai/config.json", - "provider": {}, - "permission": {}, - "mcp": {}, - "tools": { - "github-triage": false, - "github-pr-search": false, - }, -} diff --git a/reference/opencode-1.15.5/.opencode/plugins/smoke-theme.json b/reference/opencode-1.15.5/.opencode/plugins/smoke-theme.json deleted file mode 100755 index 6e4595d..0000000 --- a/reference/opencode-1.15.5/.opencode/plugins/smoke-theme.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "$schema": "https://opencode.ai/theme.json", - "defs": { - "nord0": "#2E3440", - "nord1": "#3B4252", - "nord2": "#434C5E", - "nord3": "#4C566A", - "nord4": "#D8DEE9", - "nord5": "#E5E9F0", - "nord6": "#ECEFF4", - "nord7": "#8FBCBB", - "nord8": "#88C0D0", - "nord9": "#81A1C1", - "nord10": "#5E81AC", - "nord11": "#BF616A", - "nord12": "#D08770", - "nord13": "#EBCB8B", - "nord14": "#A3BE8C", - "nord15": "#B48EAD" - }, - "theme": { - "primary": { - "dark": "nord10", - "light": "nord9" - }, - "secondary": { - "dark": "nord9", - "light": "nord9" - }, - "accent": { - "dark": "nord7", - "light": "nord7" - }, - "error": { - "dark": "nord11", - "light": "nord11" - }, - "warning": { - "dark": "nord12", - "light": "nord12" - }, - "success": { - "dark": "nord14", - "light": "nord14" - }, - "info": { - "dark": "nord8", - "light": "nord10" - }, - "text": { - "dark": "nord6", - "light": "nord0" - }, - "textMuted": { - "dark": "#8B95A7", - "light": "nord1" - }, - "background": { - "dark": "nord0", - "light": "nord6" - }, - "backgroundPanel": { - "dark": "nord1", - "light": "nord5" - }, - "backgroundElement": { - "dark": "nord2", - "light": "nord4" - }, - "border": { - "dark": "nord2", - "light": "nord3" - }, - "borderActive": { - "dark": "nord3", - "light": "nord2" - }, - "borderSubtle": { - "dark": "nord2", - "light": "nord3" - }, - "diffAdded": { - "dark": "nord14", - "light": "nord14" - }, - "diffRemoved": { - "dark": "nord11", - "light": "nord11" - }, - "diffContext": { - "dark": "#8B95A7", - "light": "nord3" - }, - "diffHunkHeader": { - "dark": "#8B95A7", - "light": "nord3" - }, - "diffHighlightAdded": { - "dark": "nord14", - "light": "nord14" - }, - "diffHighlightRemoved": { - "dark": "nord11", - "light": "nord11" - }, - "diffAddedBg": { - "dark": "#36413C", - "light": "#E6EBE7" - }, - "diffRemovedBg": { - "dark": "#43393D", - "light": "#ECE6E8" - }, - "diffContextBg": { - "dark": "nord1", - "light": "nord5" - }, - "diffLineNumber": { - "dark": "nord2", - "light": "nord4" - }, - "diffAddedLineNumberBg": { - "dark": "#303A35", - "light": "#DDE4DF" - }, - "diffRemovedLineNumberBg": { - "dark": "#3C3336", - "light": "#E4DDE0" - }, - "markdownText": { - "dark": "nord4", - "light": "nord0" - }, - "markdownHeading": { - "dark": "nord8", - "light": "nord10" - }, - "markdownLink": { - "dark": "nord9", - "light": "nord9" - }, - "markdownLinkText": { - "dark": "nord7", - "light": "nord7" - }, - "markdownCode": { - "dark": "nord14", - "light": "nord14" - }, - "markdownBlockQuote": { - "dark": "#8B95A7", - "light": "nord3" - }, - "markdownEmph": { - "dark": "nord12", - "light": "nord12" - }, - "markdownStrong": { - "dark": "nord13", - "light": "nord13" - }, - "markdownHorizontalRule": { - "dark": "#8B95A7", - "light": "nord3" - }, - "markdownListItem": { - "dark": "nord8", - "light": "nord10" - }, - "markdownListEnumeration": { - "dark": "nord7", - "light": "nord7" - }, - "markdownImage": { - "dark": "nord9", - "light": "nord9" - }, - "markdownImageText": { - "dark": "nord7", - "light": "nord7" - }, - "markdownCodeBlock": { - "dark": "nord4", - "light": "nord0" - }, - "syntaxComment": { - "dark": "#8B95A7", - "light": "nord3" - }, - "syntaxKeyword": { - "dark": "nord9", - "light": "nord9" - }, - "syntaxFunction": { - "dark": "nord8", - "light": "nord8" - }, - "syntaxVariable": { - "dark": "nord7", - "light": "nord7" - }, - "syntaxString": { - "dark": "nord14", - "light": "nord14" - }, - "syntaxNumber": { - "dark": "nord15", - "light": "nord15" - }, - "syntaxType": { - "dark": "nord7", - "light": "nord7" - }, - "syntaxOperator": { - "dark": "nord9", - "light": "nord9" - }, - "syntaxPunctuation": { - "dark": "nord4", - "light": "nord0" - } - } -} diff --git a/reference/opencode-1.15.5/.opencode/plugins/tui-smoke.tsx b/reference/opencode-1.15.5/.opencode/plugins/tui-smoke.tsx deleted file mode 100755 index 2d3095a..0000000 --- a/reference/opencode-1.15.5/.opencode/plugins/tui-smoke.tsx +++ /dev/null @@ -1,1019 +0,0 @@ -/** @jsxImportSource @opentui/solid */ -import { useTerminalDimensions, type JSX } from "@opentui/solid" -import { useBindings, useKeymapSelector } from "@opentui/keymap/solid" -import { RGBA, VignetteEffect, type KeyEvent, type Renderable } from "@opentui/core" -import { createBindingLookup, type BindingConfig } from "@opentui/keymap/extras" -import type { TuiPlugin, TuiPluginApi, TuiPluginMeta, TuiPluginModule, TuiSlotPlugin } from "@opencode-ai/plugin/tui" - -const tabs = ["overview", "counter", "help"] -const command = { - modal: "smoke_modal", - screen: "smoke_screen", - alert: "smoke_alert", - confirm: "smoke_confirm", - prompt: "smoke_prompt", - select: "smoke_select", - host: "smoke_host", - home: "smoke_home", - toast: "smoke_toast", - dialog_close: "smoke_dialog_close", - local_push: "smoke_local_push", - local_pop: "smoke_local_pop", - screen_home: "smoke_screen_home", - screen_left: "smoke_screen_left", - screen_right: "smoke_screen_right", - screen_up: "smoke_screen_up", - screen_down: "smoke_screen_down", - screen_modal: "smoke_screen_modal", - screen_local: "smoke_screen_local", - screen_host: "smoke_screen_host", - screen_alert: "smoke_screen_alert", - screen_confirm: "smoke_screen_confirm", - screen_prompt: "smoke_screen_prompt", - screen_select: "smoke_screen_select", - modal_accept: "smoke_modal_accept", - modal_close: "smoke_modal_close", -} - -type SmokeBindings = BindingConfig - -const defaultKeymap = { - [command.modal]: "ctrl+shift+m", - [command.screen]: "ctrl+shift+o", - [command.dialog_close]: "escape", - [command.local_push]: "enter,return", - [command.local_pop]: "escape,q,backspace", - [command.screen_home]: "escape,ctrl+h", - [command.screen_left]: "left,h", - [command.screen_right]: "right,l", - [command.screen_up]: "up,k", - [command.screen_down]: "down,j", - [command.screen_modal]: "ctrl+shift+m", - [command.screen_local]: "x", - [command.screen_host]: "z", - [command.screen_alert]: "a", - [command.screen_confirm]: "c", - [command.screen_prompt]: "p", - [command.screen_select]: "s", - [command.modal_accept]: "enter,return", - [command.modal_close]: "escape", -} - -const pick = (value: unknown, fallback: string) => { - if (typeof value !== "string") return fallback - if (!value.trim()) return fallback - return value -} - -const num = (value: unknown, fallback: number) => { - if (typeof value !== "number") return fallback - return value -} - -const record = (value: unknown): value is Record => - !!value && typeof value === "object" && !Array.isArray(value) - -type Cfg = { - label: string - route: string - vignette: number - keybinds: SmokeBindings | undefined -} - -type Route = { - modal: string - screen: string -} - -type State = { - tab: number - count: number - source: string - note: string - selected: string - local: number -} - -const cfg = (options: Record | undefined) => { - return { - label: pick(options?.label, "smoke"), - route: pick(options?.route, "workspace-smoke"), - vignette: Math.max(0, num(options?.vignette, 0.35)), - keybinds: record(options?.keybinds) ? (options.keybinds as SmokeBindings) : undefined, - } -} - -const names = (input: Cfg) => { - return { - modal: `${input.route}.modal`, - screen: `${input.route}.screen`, - } -} - -function createKeys(input: SmokeBindings | undefined) { - return createBindingLookup({ ...defaultKeymap, ...input }) -} - -type Keys = ReturnType - -const ui = { - panel: "#1d1d1d", - border: "#4a4a4a", - text: "#f0f0f0", - muted: "#a5a5a5", - accent: "#5f87ff", -} - -type Color = RGBA | string - -const ink = (map: Record, name: string, fallback: string): Color => { - const value = map[name] - if (typeof value === "string") return value - if (value instanceof RGBA) return value - return fallback -} - -const look = (map: Record) => { - return { - panel: ink(map, "backgroundPanel", ui.panel), - border: ink(map, "border", ui.border), - text: ink(map, "text", ui.text), - muted: ink(map, "textMuted", ui.muted), - accent: ink(map, "primary", ui.accent), - selected: ink(map, "selectedListItemText", ui.text), - } -} - -const tone = (api: TuiPluginApi) => { - return look(api.theme.current) -} - -type Skin = { - panel: Color - border: Color - text: Color - muted: Color - accent: Color - selected: Color -} - -const Btn = (props: { txt: string; run: () => void; skin: Skin; on?: boolean }) => { - return ( - { - props.run() - }} - backgroundColor={props.on ? props.skin.accent : props.skin.border} - paddingLeft={1} - paddingRight={1} - > - {props.txt} - - ) -} - -const parse = (params: Record | undefined) => { - const tab = typeof params?.tab === "number" ? params.tab : 0 - const count = typeof params?.count === "number" ? params.count : 0 - const source = typeof params?.source === "string" ? params.source : "unknown" - const note = typeof params?.note === "string" ? params.note : "" - const selected = typeof params?.selected === "string" ? params.selected : "" - const local = typeof params?.local === "number" ? params.local : 0 - return { - tab: Math.max(0, Math.min(tab, tabs.length - 1)), - count, - source, - note, - selected, - local: Math.max(0, local), - } -} - -const current = (api: TuiPluginApi, route: Route) => { - const value = api.route.current - const ok = Object.values(route).includes(value.name) - if (!ok) return parse(undefined) - if (!("params" in value)) return parse(undefined) - return parse(value.params) -} - -const opts = [ - { - title: "Overview", - value: 0, - description: "Switch to overview tab", - }, - { - title: "Counter", - value: 1, - description: "Switch to counter tab", - }, - { - title: "Help", - value: 2, - description: "Switch to help tab", - }, -] - -const host = (api: TuiPluginApi, input: Cfg, skin: Skin) => { - api.ui.dialog.setSize("medium") - api.ui.dialog.replace(() => ( - - - {input.label} host overlay - - Using api.ui.dialog stack with built-in backdrop - esc closes · depth {api.ui.dialog.depth} - - api.ui.dialog.clear()} skin={skin} on /> - - - )) -} - -const warn = (api: TuiPluginApi, route: Route, value: State) => { - const DialogAlert = api.ui.DialogAlert - api.ui.dialog.setSize("medium") - api.ui.dialog.replace(() => ( - api.route.navigate(route.screen, { ...value, source: "alert" })} - /> - )) -} - -const check = (api: TuiPluginApi, route: Route, value: State) => { - const DialogConfirm = api.ui.DialogConfirm - api.ui.dialog.setSize("medium") - api.ui.dialog.replace(() => ( - api.route.navigate(route.screen, { ...value, count: value.count + 1, source: "confirm" })} - onCancel={() => api.route.navigate(route.screen, { ...value, source: "confirm-cancel" })} - /> - )) -} - -const entry = (api: TuiPluginApi, route: Route, value: State) => { - const DialogPrompt = api.ui.DialogPrompt - api.ui.dialog.setSize("medium") - api.ui.dialog.replace(() => ( - { - api.ui.dialog.clear() - api.route.navigate(route.screen, { ...value, note, source: "prompt" }) - }} - onCancel={() => { - api.ui.dialog.clear() - api.route.navigate(route.screen, value) - }} - /> - )) -} - -const picker = (api: TuiPluginApi, route: Route, value: State) => { - const DialogSelect = api.ui.DialogSelect - api.ui.dialog.setSize("medium") - api.ui.dialog.replace(() => ( - { - api.ui.dialog.clear() - api.route.navigate(route.screen, { - ...value, - tab: typeof item.value === "number" ? item.value : value.tab, - selected: item.title, - source: "select", - }) - }} - /> - )) -} - -const Screen = (props: { - api: TuiPluginApi - input: Cfg - route: Route - keys: Keys - meta: TuiPluginMeta - params?: Record -}) => { - const dim = useTerminalDimensions() - const value = parse(props.params) - const skin = tone(props.api) - const set = (local: number, base?: State) => { - const next = base ?? current(props.api, props.route) - props.api.route.navigate(props.route.screen, { ...next, local: Math.max(0, local), source: "local" }) - } - const push = (base?: State) => { - const next = base ?? current(props.api, props.route) - set(next.local + 1, next) - } - const open = () => { - const next = current(props.api, props.route) - if (next.local > 0) return - set(1, next) - } - const pop = (base?: State) => { - const next = base ?? current(props.api, props.route) - set(Math.max(0, next.local - 1), next) - } - const show = () => { - setTimeout(() => { - open() - }, 0) - } - const screenActive = () => props.api.route.current.name === props.route.screen - - useBindings(() => ({ - enabled: () => screenActive() && props.api.ui.dialog.open, - commands: [ - { - name: command.dialog_close, - run() { - props.api.ui.dialog.clear() - }, - }, - ], - bindings: props.keys.gather("smoke.dialog", [command.dialog_close]), - })) - - useBindings(() => ({ - enabled: () => screenActive() && !props.api.ui.dialog.open && current(props.api, props.route).local > 0, - commands: [ - { - name: command.local_push, - run() { - push(current(props.api, props.route)) - }, - }, - { - name: command.local_pop, - run() { - pop(current(props.api, props.route)) - }, - }, - ], - bindings: props.keys.gather("smoke.local", [command.local_push, command.local_pop]), - })) - - useBindings(() => ({ - enabled: () => screenActive() && !props.api.ui.dialog.open && current(props.api, props.route).local === 0, - commands: [ - { - name: command.screen_home, - run() { - props.api.route.navigate("home") - }, - }, - { - name: command.screen_left, - run() { - const next = current(props.api, props.route) - props.api.route.navigate(props.route.screen, { ...next, tab: (next.tab - 1 + tabs.length) % tabs.length }) - }, - }, - { - name: command.screen_right, - run() { - const next = current(props.api, props.route) - props.api.route.navigate(props.route.screen, { ...next, tab: (next.tab + 1) % tabs.length }) - }, - }, - { - name: command.screen_up, - run() { - const next = current(props.api, props.route) - props.api.route.navigate(props.route.screen, { ...next, count: next.count + 1 }) - }, - }, - { - name: command.screen_down, - run() { - const next = current(props.api, props.route) - props.api.route.navigate(props.route.screen, { ...next, count: next.count - 1 }) - }, - }, - { - name: command.screen_modal, - run() { - props.api.route.navigate(props.route.modal, current(props.api, props.route)) - }, - }, - { - name: command.screen_local, - run() { - open() - }, - }, - { - name: command.screen_host, - run() { - host(props.api, props.input, skin) - }, - }, - { - name: command.screen_alert, - run() { - warn(props.api, props.route, current(props.api, props.route)) - }, - }, - { - name: command.screen_confirm, - run() { - check(props.api, props.route, current(props.api, props.route)) - }, - }, - { - name: command.screen_prompt, - run() { - entry(props.api, props.route, current(props.api, props.route)) - }, - }, - { - name: command.screen_select, - run() { - picker(props.api, props.route, current(props.api, props.route)) - }, - }, - ], - bindings: props.keys.gather("smoke.screen", [ - command.screen_home, - command.screen_left, - command.screen_right, - command.screen_up, - command.screen_down, - command.screen_modal, - command.screen_local, - command.screen_host, - command.screen_alert, - command.screen_confirm, - command.screen_prompt, - command.screen_select, - ]), - })) - const shortcuts = useKeymapSelector((keymap) => { - const bindings = keymap.getCommandBindings({ - visibility: "registered", - commands: [ - command.screen_home, - command.screen_up, - command.screen_down, - command.screen_modal, - command.screen_alert, - command.screen_confirm, - command.screen_prompt, - command.screen_select, - command.screen_local, - command.screen_host, - command.local_push, - command.local_pop, - ], - }) - - return { - screen_home: props.api.keys.formatBindings(bindings.get(command.screen_home)) ?? "", - screen_up: props.api.keys.formatBindings(bindings.get(command.screen_up)) ?? "", - screen_down: props.api.keys.formatBindings(bindings.get(command.screen_down)) ?? "", - screen_modal: props.api.keys.formatBindings(bindings.get(command.screen_modal)) ?? "", - screen_alert: props.api.keys.formatBindings(bindings.get(command.screen_alert)) ?? "", - screen_confirm: props.api.keys.formatBindings(bindings.get(command.screen_confirm)) ?? "", - screen_prompt: props.api.keys.formatBindings(bindings.get(command.screen_prompt)) ?? "", - screen_select: props.api.keys.formatBindings(bindings.get(command.screen_select)) ?? "", - screen_local: props.api.keys.formatBindings(bindings.get(command.screen_local)) ?? "", - screen_host: props.api.keys.formatBindings(bindings.get(command.screen_host)) ?? "", - local_push: props.api.keys.formatBindings(bindings.get(command.local_push)) ?? "", - local_pop: props.api.keys.formatBindings(bindings.get(command.local_pop)) ?? "", - } - }) - - return ( - - - - - {props.input.label} screen - plugin route - - {shortcuts().screen_home} home - - - - {tabs.map((item, i) => { - const on = value.tab === i - return ( - props.api.route.navigate(props.route.screen, { ...value, tab: i })} - skin={skin} - on={on} - /> - ) - })} - - - - {value.tab === 0 ? ( - - Route: {props.route.screen} - plugin state: {props.meta.state} - - first: {props.meta.state === "first" ? "yes" : "no"} · updated:{" "} - {props.meta.state === "updated" ? "yes" : "no"} · loads: {props.meta.load_count} - - plugin source: {props.meta.source} - source: {value.source} - note: {value.note || "(none)"} - selected: {value.selected || "(none)"} - local stack depth: {value.local} - host stack open: {props.api.ui.dialog.open ? "yes" : "no"} - - ) : null} - - {value.tab === 1 ? ( - - Counter: {value.count} - - {shortcuts().screen_up} / {shortcuts().screen_down} change value - - - ) : null} - - {value.tab === 2 ? ( - - - {shortcuts().screen_modal} modal | {shortcuts().screen_alert} alert | {shortcuts().screen_confirm}{" "} - confirm | {shortcuts().screen_prompt} prompt | {shortcuts().screen_select} select - - - {shortcuts().screen_local} local stack | {shortcuts().screen_host} host stack - - - local open: {shortcuts().local_push} push nested · {shortcuts().local_pop} close - - {shortcuts().screen_home} returns home - - ) : null} - - - - props.api.route.navigate("home")} skin={skin} /> - props.api.route.navigate(props.route.modal, value)} skin={skin} on /> - - host(props.api, props.input, skin)} skin={skin} /> - warn(props.api, props.route, value)} skin={skin} /> - check(props.api, props.route, value)} skin={skin} /> - entry(props.api, props.route, value)} skin={skin} /> - picker(props.api, props.route, value)} skin={skin} /> - - - - 0} - width={dim().width} - height={dim().height} - alignItems="center" - position="absolute" - zIndex={3000} - paddingTop={dim().height / 4} - left={0} - top={0} - backgroundColor={RGBA.fromInts(0, 0, 0, 160)} - onMouseUp={() => { - pop() - }} - > - { - evt.stopPropagation() - }} - width={60} - maxWidth={dim().width - 2} - backgroundColor={skin.panel} - border - borderColor={skin.border} - paddingTop={1} - paddingBottom={1} - paddingLeft={2} - paddingRight={2} - gap={1} - flexDirection="column" - > - - {props.input.label} local overlay - - Plugin-owned stack depth: {value.local} - - {shortcuts().local_push} push nested · {shortcuts().local_pop} pop/close - - - - - - - - - ) -} - -const Modal = (props: { - api: TuiPluginApi - input: Cfg - route: Route - keys: Keys - params?: Record -}) => { - const Dialog = props.api.ui.Dialog - const value = parse(props.params) - const skin = tone(props.api) - - useBindings(() => ({ - enabled: () => props.api.route.current.name === props.route.modal, - commands: [ - { - name: command.modal_accept, - run() { - props.api.route.navigate(props.route.screen, { ...parse(props.params), source: "modal" }) - }, - }, - { - name: command.modal_close, - run() { - props.api.route.navigate("home") - }, - }, - ], - bindings: props.keys.gather("smoke.modal", [command.modal_accept, command.modal_close]), - })) - const shortcuts = useKeymapSelector((keymap) => { - const bindings = keymap.getCommandBindings({ - visibility: "registered", - commands: [command.modal, command.screen, command.modal_accept, command.modal_close], - }) - - return { - modal: props.api.keys.formatBindings(bindings.get(command.modal)) ?? "", - screen: props.api.keys.formatBindings(bindings.get(command.screen)) ?? "", - modal_accept: props.api.keys.formatBindings(bindings.get(command.modal_accept)) ?? "", - modal_close: props.api.keys.formatBindings(bindings.get(command.modal_close)) ?? "", - } - }) - - return ( - - props.api.route.navigate("home")}> - - - {props.input.label} modal - - {shortcuts().modal} modal command - {shortcuts().screen} screen command - - {shortcuts().modal_accept} opens screen · {shortcuts().modal_close} closes - - - props.api.route.navigate(props.route.screen, { ...value, source: "modal" })} - skin={skin} - on - /> - props.api.route.navigate("home")} skin={skin} /> - - - - - ) -} - -const home = (api: TuiPluginApi, input: Cfg) => ({ - slots: { - home_logo(ctx) { - const map = ctx.theme.current - const skin = look(map) - const art = [ - " $$\\", - " $$ |", - " $$$$$$$\\ $$$$$$\\$$$$\\ $$$$$$\\ $$ | $$\\ $$$$$$\\", - "$$ _____|$$ _$$ _$$\\ $$ __$$\\ $$ | $$ |$$ __$$\\", - "\\$$$$$$\\ $$ / $$ / $$ |$$ / $$ |$$$$$$ / $$$$$$$$ |", - " \\____$$\\ $$ | $$ | $$ |$$ | $$ |$$ _$$< $$ ____|", - "$$$$$$$ |$$ | $$ | $$ |\\$$$$$$ |$$ | \\$$\\ \\$$$$$$$\\", - "\\_______/ \\__| \\__| \\__| \\______/ \\__| \\__| \\_______|", - ] - const fill = [ - skin.accent, - skin.muted, - ink(map, "info", ui.accent), - skin.text, - ink(map, "success", ui.accent), - ink(map, "warning", ui.accent), - ink(map, "secondary", ui.accent), - ink(map, "error", ui.accent), - ] - - return ( - - {art.map((line, i) => ( - {line} - ))} - - ) - }, - home_prompt(ctx, value) { - const skin = look(ctx.theme.current) - const Prompt = api.ui.Prompt - const Slot = api.ui.Slot - const normal = [ - `[SMOKE] route check for ${input.label}`, - "[SMOKE] confirm home_prompt slot override", - "[SMOKE] verify prompt-right slot passthrough", - ] - const shell = ["printf '[SMOKE] home prompt\n'", "git status --short", "bun --version"] - const hint = ( - - - smoke home prompt - - - ) - - return ( - - - - - } - placeholders={{ normal, shell }} - /> - ) - }, - home_prompt_right(ctx, value) { - const skin = look(ctx.theme.current) - const id = value.workspace_id?.slice(0, 8) ?? "none" - return ( - - {input.label} home:{id} - - ) - }, - session_prompt_right(ctx, value) { - const skin = look(ctx.theme.current) - return ( - - {input.label} session:{value.session_id.slice(0, 8)} - - ) - }, - smoke_prompt_right(ctx, value) { - const skin = look(ctx.theme.current) - const id = typeof value.workspace_id === "string" ? value.workspace_id.slice(0, 8) : "none" - const label = typeof value.label === "string" ? value.label : input.label - return ( - - {label} custom:{id} - - ) - }, - home_bottom(ctx) { - const skin = look(ctx.theme.current) - const text = "extra content in the unified home bottom slot" - - return ( - - - - {input.label} {text} - - - - ) - }, - }, -}) - -const block = (input: Cfg, order: number, title: string, text: string): TuiSlotPlugin => ({ - order, - slots: { - sidebar_content(ctx, value) { - const skin = look(ctx.theme.current) - - return ( - - - {title} - - {text} - - {input.label} order {order} · session {value.session_id.slice(0, 8)} - - - ) - }, - }, -}) - -const slot = (api: TuiPluginApi, input: Cfg): TuiSlotPlugin[] => [ - home(api, input), - block(input, 50, "Smoke above", "renders above internal sidebar blocks"), - block(input, 250, "Smoke between", "renders between internal sidebar blocks"), - block(input, 650, "Smoke below", "renders below internal sidebar blocks"), -] - -const reg = (api: TuiPluginApi, input: Cfg, keys: Keys) => { - const route = names(input) - api.keymap.registerLayer({ - commands: [ - { - name: command.modal, - title: `${input.label} modal`, - category: "Plugin", - namespace: "palette", - slashName: "smoke", - run() { - api.route.navigate(route.modal, { source: "command" }) - }, - }, - { - name: command.screen, - title: `${input.label} screen`, - category: "Plugin", - namespace: "palette", - slashName: "smoke-screen", - run() { - api.route.navigate(route.screen, { source: "command", tab: 0, count: 0 }) - }, - }, - { - name: command.alert, - title: `${input.label} alert dialog`, - category: "Plugin", - namespace: "palette", - slashName: "smoke-alert", - run() { - warn(api, route, current(api, route)) - }, - }, - { - name: command.confirm, - title: `${input.label} confirm dialog`, - category: "Plugin", - namespace: "palette", - slashName: "smoke-confirm", - run() { - check(api, route, current(api, route)) - }, - }, - { - name: command.prompt, - title: `${input.label} prompt dialog`, - category: "Plugin", - namespace: "palette", - slashName: "smoke-prompt", - run() { - entry(api, route, current(api, route)) - }, - }, - { - name: command.select, - title: `${input.label} select dialog`, - category: "Plugin", - namespace: "palette", - slashName: "smoke-select", - run() { - picker(api, route, current(api, route)) - }, - }, - { - name: command.host, - title: `${input.label} host overlay`, - category: "Plugin", - namespace: "palette", - slashName: "smoke-host", - run() { - host(api, input, tone(api)) - }, - }, - { - name: command.home, - title: `${input.label} go home`, - category: "Plugin", - namespace: "palette", - enabled: () => api.route.current.name !== "home", - run() { - api.route.navigate("home") - }, - }, - { - name: command.toast, - title: `${input.label} toast`, - category: "Plugin", - namespace: "palette", - run() { - api.ui.toast({ - variant: "info", - title: "Smoke", - message: "Plugin toast works", - duration: 2000, - }) - }, - }, - ], - bindings: keys.gather("smoke.global", [ - command.modal, - command.screen, - command.alert, - command.confirm, - command.prompt, - command.select, - command.host, - command.home, - command.toast, - ]), - }) -} - -const tui: TuiPlugin = async (api, options, meta) => { - if (options?.enabled === false) return - - await api.theme.install("./smoke-theme.json") - api.theme.set("smoke-theme") - - const value = cfg(options) - const route = names(value) - const keys = createKeys(value.keybinds) - const fx = new VignetteEffect(value.vignette) - const post = fx.apply.bind(fx) - api.renderer.addPostProcessFn(post) - api.lifecycle.onDispose(() => { - api.renderer.removePostProcessFn(post) - }) - - api.route.register([ - { - name: route.screen, - render: ({ params }) => , - }, - { - name: route.modal, - render: ({ params }) => , - }, - ]) - - reg(api, value, keys) - for (const item of slot(api, value)) { - api.slots.register(item) - } -} - -const plugin: TuiPluginModule & { id: string } = { - id: "tui-smoke", - tui, -} - -export default plugin diff --git a/reference/opencode-1.15.5/.opencode/skills/effect/SKILL.md b/reference/opencode-1.15.5/.opencode/skills/effect/SKILL.md deleted file mode 100755 index 3a44fa8..0000000 --- a/reference/opencode-1.15.5/.opencode/skills/effect/SKILL.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: effect -description: Work with Effect v4 / effect-smol TypeScript code in this repo ---- - -# Effect - -This codebase uses Effect for typed, composable TypeScript services, schemas, and workflows. - -## Source Of Truth - -Use the current Effect v4 / effect-smol source, not memory or older Effect v2/v3 examples. - -1. If `.opencode/references/effect-smol` is missing, clone `https://github.com/Effect-TS/effect-smol` there. Do this in the project, not in the skill folder. -2. Search `.opencode/references/effect-smol` for exact APIs, examples, tests, and naming patterns before answering or implementing Effect-specific code. -3. Also inspect existing repo code for local house style before introducing new patterns. -4. Prefer answers and implementations backed by specific source files or nearby repo examples. - -## Guidelines - -- Prefer current Effect v4 APIs and project-local patterns over old blog posts, examples, or package-memory guesses. -- Use `Effect.gen(function* () { ... })` for multi-step workflows. -- Use `Effect.fn("Name")` or `Effect.fnUntraced(...)` for named effects when adding reusable service methods or important workflows. -- Prefer Effect `Schema` for API and domain data shapes. Use branded schemas for IDs and `Schema.TaggedErrorClass` for typed domain errors when modeling new error surfaces. -- Keep HTTP handlers thin: decode input, read request context, call services, and map transport errors. Put business rules in services. -- In Effect service code, prefer Effect-aware platform abstractions and dependencies over ad hoc promises where the surrounding code already does so. -- Keep layer composition explicit. Avoid broad hidden provisioning that makes missing dependencies hard to see. -- In tests, prefer the repo's existing Effect test helpers and live tests for filesystem, git, child process, locks, or timing behavior. -- Do not introduce `any`, non-null assertions, unchecked casts, or older Effect APIs just to satisfy types. -- Do not answer from memory. Verify against `.opencode/references/effect-smol` or nearby code first. - -## Testing Patterns - -- Use `testEffect(...)` from `packages/opencode/test/lib/effect.ts` for tests that exercise Effect services, layers, runtime context, scoped resources, or platform integrations. -- Use `it.live(...)` for filesystem, git repositories, HTTP servers, sockets, child processes, locks, real time, and other live platform behavior. -- Run tests from package directories such as `packages/opencode`; never run package tests from the repo root. -- Prefer explicit test layers over ad hoc managed runtimes. Keep dependency provisioning visible in the test file. -- Use scoped fixtures and finalizers for resources that must be cleaned up, including temporary directories, flags, databases, fibers, servers, and global state. diff --git a/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/DEEPENING.md b/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/DEEPENING.md deleted file mode 100755 index c52fdfd..0000000 --- a/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/DEEPENING.md +++ /dev/null @@ -1,37 +0,0 @@ -# Deepening - -How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**. - -## Dependency categories - -When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam. - -### 1. In-process - -Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed. - -### 2. Local-substitutable - -Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface. - -### 3. Remote but owned (Ports & Adapters) - -Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter. - -Recommendation shape: _"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."_ - -### 4. True external (Mock) - -Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter. - -## Seam discipline - -- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection. -- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them. - -## Testing strategy: replace, don't layer - -- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them. -- Write new tests at the deepened module's interface. The **interface is the test surface**. -- Tests assert on observable outcomes through the interface, not internal state. -- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface. diff --git a/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/INTERFACE-DESIGN.md b/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/INTERFACE-DESIGN.md deleted file mode 100755 index 3197723..0000000 --- a/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +++ /dev/null @@ -1,44 +0,0 @@ -# Interface Design - -When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best. - -Uses the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**, **leverage**. - -## Process - -### 1. Frame the problem space - -Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate: - -- The constraints any new interface would need to satisfy -- The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md)) -- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete - -Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel. - -### 2. Spawn sub-agents - -Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module. - -Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint: - -- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point." -- Agent 2: "Maximise flexibility — support many use cases and extension." -- Agent 3: "Optimise for the most common caller — make the default case trivial." -- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies." - -Include both [LANGUAGE.md](LANGUAGE.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language. - -Each sub-agent outputs: - -1. Interface (types, methods, params — plus invariants, ordering, error modes) -2. Usage example showing how callers use it -3. What the implementation hides behind the seam -4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md)) -5. Trade-offs — where leverage is high, where it's thin - -### 3. Present and compare - -Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**. - -After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not a menu. diff --git a/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/LANGUAGE.md b/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/LANGUAGE.md deleted file mode 100755 index dd9b60f..0000000 --- a/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/LANGUAGE.md +++ /dev/null @@ -1,53 +0,0 @@ -# Language - -Shared vocabulary for every suggestion this skill makes. Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point. - -## Terms - -**Module** -Anything with an interface and an implementation. Deliberately scale-agnostic — applies equally to a function, class, package, or tier-spanning slice. -_Avoid_: unit, component, service. - -**Interface** -Everything a caller must know to use the module correctly. Includes the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. -_Avoid_: API, signature (too narrow — those refer only to the type-level surface). - -**Implementation** -What's inside a module — its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise. - -**Depth** -Leverage at the interface — the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface. A module is **shallow** when the interface is nearly as complex as the implementation. - -**Seam** _(from Michael Feathers)_ -A place where you can alter behaviour without editing in that place. The _location_ at which a module's interface lives. Choosing where to put the seam is its own design decision, distinct from what goes behind it. -_Avoid_: boundary (overloaded with DDD's bounded context). - -**Adapter** -A concrete thing that satisfies an interface at a seam. Describes _role_ (what slot it fills), not substance (what's inside). - -**Leverage** -What callers get from depth. More capability per unit of interface they have to learn. One implementation pays back across N call sites and M tests. - -**Locality** -What maintainers get from depth. Change, bugs, knowledge, and verification concentrate at one place rather than spreading across callers. Fix once, fixed everywhere. - -## Principles - -- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface. -- **The deletion test.** Imagine deleting the module. If complexity vanishes, the module wasn't hiding anything (it was a pass-through). If complexity reappears across N callers, the module was earning its keep. -- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test _past_ the interface, the module is probably the wrong shape. -- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it. - -## Relationships - -- A **Module** has exactly one **Interface** (the surface it presents to callers and tests). -- **Depth** is a property of a **Module**, measured against its **Interface**. -- A **Seam** is where a **Module**'s **Interface** lives. -- An **Adapter** sits at a **Seam** and satisfies the **Interface**. -- **Depth** produces **Leverage** for callers and **Locality** for maintainers. - -## Rejected framings - -- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead. -- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know. -- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**. diff --git a/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/SKILL.md b/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/SKILL.md deleted file mode 100755 index 05984a6..0000000 --- a/reference/opencode-1.15.5/.opencode/skills/improve-codebase-architecture/SKILL.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -name: improve-codebase-architecture -description: Find deepening opportunities in a codebase, informed by the domain language in CONTEXT.md and the decisions in docs/adr/. Use when the user wants to improve architecture, find refactoring opportunities, consolidate tightly-coupled modules, or make a codebase more testable and AI-navigable. ---- - -# Improve Codebase Architecture - -Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability. - -## Glossary - -Use these terms exactly in every suggestion. Consistent language is the point — don't drift into "component," "service," "API," or "boundary." Full definitions in [LANGUAGE.md](LANGUAGE.md). - -- **Module** — anything with an interface and an implementation (function, class, package, slice). -- **Interface** — everything a caller must know to use the module: types, invariants, error modes, ordering, config. Not just the type signature. -- **Implementation** — the code inside. -- **Depth** — leverage at the interface: a lot of behaviour behind a small interface. **Deep** = high leverage. **Shallow** = interface nearly as complex as the implementation. -- **Seam** — where an interface lives; a place behaviour can be altered without editing in place. (Use this, not "boundary.") -- **Adapter** — a concrete thing satisfying an interface at a seam. -- **Leverage** — what callers get from depth. -- **Locality** — what maintainers get from depth: change, bugs, knowledge concentrated in one place. - -Key principles (see [LANGUAGE.md](LANGUAGE.md) for the full list): - -- **Deletion test**: imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep. -- **The interface is the test surface.** -- **One adapter = hypothetical seam. Two adapters = real seam.** - -This skill is _informed_ by the project's domain model. The domain language gives names to good seams; ADRs record decisions the skill should not re-litigate. - -## Process - -### 1. Explore - -Read the project's domain glossary and any ADRs in the area you're touching first. - -Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction: - -- Where does understanding one concept require bouncing between many small modules? -- Where are modules **shallow** — interface nearly as complex as the implementation? -- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)? -- Where do tightly-coupled modules leak across their seams? -- Which parts of the codebase are untested, or hard to test through their current interface? - -Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want. - -### 2. Present candidates - -Present a numbered list of deepening opportunities. For each candidate: - -- **Files** — which files/modules are involved -- **Problem** — why the current architecture is causing friction -- **Solution** — plain English description of what would change -- **Benefits** — explained in terms of locality and leverage, and also in how tests would improve - -**Use CONTEXT.md vocabulary for the domain, and [LANGUAGE.md](LANGUAGE.md) vocabulary for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module" — not "the FooBarHandler," and not "the Order service." - -**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly (e.g. _"contradicts ADR-0007 — but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids. - -Do NOT propose interfaces yet. Ask the user: "Which of these would you like to explore?" - -### 3. Grilling loop - -Once the user picks a candidate, drop into a grilling conversation. Walk the design tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive. - -Side effects happen inline as decisions crystallize: - -- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `/grill-with-docs` (see [CONTEXT-FORMAT.md](../grill-with-docs/CONTEXT-FORMAT.md)). Create the file lazily if it doesn't exist. -- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there. -- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See [ADR-FORMAT.md](../grill-with-docs/ADR-FORMAT.md). -- **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md). diff --git a/reference/opencode-1.15.5/.opencode/themes/mytheme.json b/reference/opencode-1.15.5/.opencode/themes/mytheme.json deleted file mode 100755 index 0e6b948..0000000 --- a/reference/opencode-1.15.5/.opencode/themes/mytheme.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "$schema": "https://opencode.ai/theme.json", - "defs": { - "nord0": "#2E3440", - "nord1": "#3B4252", - "nord2": "#434C5E", - "nord3": "#4C566A", - "nord4": "#D8DEE9", - "nord5": "#E5E9F0", - "nord6": "#ECEFF4", - "nord7": "#8FBCBB", - "nord8": "#88C0D0", - "nord9": "#81A1C1", - "nord10": "#5E81AC", - "nord11": "#BF616A", - "nord12": "#D08770", - "nord13": "#EBCB8B", - "nord14": "#A3BE8C", - "nord15": "#B48EAD" - }, - "theme": { - "primary": { - "dark": "nord8", - "light": "nord10" - }, - "secondary": { - "dark": "nord9", - "light": "nord9" - }, - "accent": { - "dark": "nord7", - "light": "nord7" - }, - "error": { - "dark": "nord11", - "light": "nord11" - }, - "warning": { - "dark": "nord12", - "light": "nord12" - }, - "success": { - "dark": "nord14", - "light": "nord14" - }, - "info": { - "dark": "nord8", - "light": "nord10" - }, - "text": { - "dark": "nord4", - "light": "nord0" - }, - "textMuted": { - "dark": "nord3", - "light": "nord1" - }, - "background": { - "dark": "nord0", - "light": "nord6" - }, - "backgroundPanel": { - "dark": "nord1", - "light": "nord5" - }, - "backgroundElement": { - "dark": "nord1", - "light": "nord4" - }, - "border": { - "dark": "nord2", - "light": "nord3" - }, - "borderActive": { - "dark": "nord3", - "light": "nord2" - }, - "borderSubtle": { - "dark": "nord2", - "light": "nord3" - }, - "diffAdded": { - "dark": "nord14", - "light": "nord14" - }, - "diffRemoved": { - "dark": "nord11", - "light": "nord11" - }, - "diffContext": { - "dark": "nord3", - "light": "nord3" - }, - "diffHunkHeader": { - "dark": "nord3", - "light": "nord3" - }, - "diffHighlightAdded": { - "dark": "nord14", - "light": "nord14" - }, - "diffHighlightRemoved": { - "dark": "nord11", - "light": "nord11" - }, - "diffAddedBg": { - "dark": "#3B4252", - "light": "#E5E9F0" - }, - "diffRemovedBg": { - "dark": "#3B4252", - "light": "#E5E9F0" - }, - "diffContextBg": { - "dark": "nord1", - "light": "nord5" - }, - "diffLineNumber": { - "dark": "#abafb7", - "light": "textMuted" - }, - "diffAddedLineNumberBg": { - "dark": "#3B4252", - "light": "#E5E9F0" - }, - "diffRemovedLineNumberBg": { - "dark": "#3B4252", - "light": "#E5E9F0" - }, - "markdownText": { - "dark": "nord4", - "light": "nord0" - }, - "markdownHeading": { - "dark": "nord8", - "light": "nord10" - }, - "markdownLink": { - "dark": "nord9", - "light": "nord9" - }, - "markdownLinkText": { - "dark": "nord7", - "light": "nord7" - }, - "markdownCode": { - "dark": "nord14", - "light": "nord14" - }, - "markdownBlockQuote": { - "dark": "nord3", - "light": "nord3" - }, - "markdownEmph": { - "dark": "nord12", - "light": "nord12" - }, - "markdownStrong": { - "dark": "nord13", - "light": "nord13" - }, - "markdownHorizontalRule": { - "dark": "nord3", - "light": "nord3" - }, - "markdownListItem": { - "dark": "nord8", - "light": "nord10" - }, - "markdownListEnumeration": { - "dark": "nord7", - "light": "nord7" - }, - "markdownImage": { - "dark": "nord9", - "light": "nord9" - }, - "markdownImageText": { - "dark": "nord7", - "light": "nord7" - }, - "markdownCodeBlock": { - "dark": "nord4", - "light": "nord0" - }, - "syntaxComment": { - "dark": "nord3", - "light": "nord3" - }, - "syntaxKeyword": { - "dark": "nord9", - "light": "nord9" - }, - "syntaxFunction": { - "dark": "nord8", - "light": "nord8" - }, - "syntaxVariable": { - "dark": "nord7", - "light": "nord7" - }, - "syntaxString": { - "dark": "nord14", - "light": "nord14" - }, - "syntaxNumber": { - "dark": "nord15", - "light": "nord15" - }, - "syntaxType": { - "dark": "nord7", - "light": "nord7" - }, - "syntaxOperator": { - "dark": "nord9", - "light": "nord9" - }, - "syntaxPunctuation": { - "dark": "nord4", - "light": "nord0" - } - } -} diff --git a/reference/opencode-1.15.5/.opencode/tool/github-pr-search.ts b/reference/opencode-1.15.5/.opencode/tool/github-pr-search.ts deleted file mode 100755 index 8bc8c55..0000000 --- a/reference/opencode-1.15.5/.opencode/tool/github-pr-search.ts +++ /dev/null @@ -1,64 +0,0 @@ -/// -import { tool } from "@opencode-ai/plugin" -async function githubFetch(endpoint: string, options: RequestInit = {}) { - const response = await fetch(`https://api.github.com${endpoint}`, { - ...options, - headers: { - Authorization: `Bearer ${process.env.GITHUB_TOKEN}`, - Accept: "application/vnd.github+json", - "Content-Type": "application/json", - ...(options.headers instanceof Headers ? Object.fromEntries(options.headers.entries()) : options.headers), - }, - }) - if (!response.ok) { - throw new Error(`GitHub API error: ${response.status} ${response.statusText}`) - } - return response.json() -} - -interface PR { - title: string - html_url: string -} - -export default tool({ - description: `Use this tool to search GitHub pull requests by title and description. - -This tool searches PRs in the anomalyco/opencode repository and returns LLM-friendly results including: -- PR number and title -- Author -- State (open/closed/merged) -- Labels -- Description snippet - -Use the query parameter to search for keywords that might appear in PR titles or descriptions.`, - args: { - query: tool.schema.string().describe("Search query for PR titles and descriptions"), - limit: tool.schema.number().describe("Maximum number of results to return").default(10), - offset: tool.schema.number().describe("Number of results to skip for pagination").default(0), - }, - async execute(args) { - const owner = "anomalyco" - const repo = "opencode" - - const page = Math.floor(args.offset / args.limit) + 1 - const searchQuery = encodeURIComponent(`${args.query} repo:${owner}/${repo} type:pr state:open`) - const result = await githubFetch( - `/search/issues?q=${searchQuery}&per_page=${args.limit}&page=${page}&sort=updated&order=desc`, - ) - - if (result.total_count === 0) { - return `No PRs found matching "${args.query}"` - } - - const prs = result.items as PR[] - - if (prs.length === 0) { - return `No other PRs found matching "${args.query}"` - } - - const formatted = prs.map((pr) => `${pr.title}\n${pr.html_url}`).join("\n\n") - - return `Found ${result.total_count} PRs (showing ${prs.length}):\n\n${formatted}` - }, -}) diff --git a/reference/opencode-1.15.5/.opencode/tool/github-triage.ts b/reference/opencode-1.15.5/.opencode/tool/github-triage.ts deleted file mode 100755 index 35db446..0000000 --- a/reference/opencode-1.15.5/.opencode/tool/github-triage.ts +++ /dev/null @@ -1,60 +0,0 @@ -/// -import { tool } from "@opencode-ai/plugin" - -const TEAM = { - tui: ["kommander", "simonklee"], - desktop_web: ["Hona", "Brendonovich"], - core: ["jlongster", "rekram1-node", "nexxeln", "kitlangton"], - inference: ["fwang", "MrMushrooooom"], - windows: ["Hona"], -} as const - -function pick(items: readonly T[]) { - return items[Math.floor(Math.random() * items.length)]! -} - -function getIssueNumber(): number { - const issue = parseInt(process.env.ISSUE_NUMBER ?? "", 10) - if (!issue) throw new Error("ISSUE_NUMBER env var not set") - return issue -} - -async function githubFetch(endpoint: string, options: RequestInit = {}) { - const response = await fetch(`https://api.github.com${endpoint}`, { - ...options, - headers: { - Authorization: `Bearer ${process.env.GITHUB_TOKEN}`, - Accept: "application/vnd.github+json", - "Content-Type": "application/json", - ...(options.headers instanceof Headers ? Object.fromEntries(options.headers.entries()) : options.headers), - }, - }) - if (!response.ok) { - throw new Error(`GitHub API error: ${response.status} ${response.statusText}`) - } - return response.json() -} - -export default tool({ - description: `Use this tool to assign a GitHub issue. - -Provide the team that should own the issue. This tool picks a random assignee from that team and does not apply labels.`, - args: { - team: tool.schema - .enum(Object.keys(TEAM) as [keyof typeof TEAM, ...(keyof typeof TEAM)[]]) - .describe("The owning team"), - }, - async execute(args) { - const issue = getIssueNumber() - const owner = "anomalyco" - const repo = "opencode" - const assignee = pick(TEAM[args.team]) - - await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, { - method: "POST", - body: JSON.stringify({ assignees: [assignee] }), - }) - - return `Assigned @${assignee} from ${args.team} to issue #${issue}` - }, -}) diff --git a/reference/opencode-1.15.5/.opencode/tui.json b/reference/opencode-1.15.5/.opencode/tui.json deleted file mode 100755 index b92e58d..0000000 --- a/reference/opencode-1.15.5/.opencode/tui.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "https://opencode.ai/tui.json", - "plugin": [ - [ - "./plugins/tui-smoke.tsx", - { - "enabled": false, - "label": "workspace", - "keybinds": { - "smoke_modal": "ctrl+alt+m", - "smoke_screen": "ctrl+alt+o", - "smoke_screen_home": "escape,ctrl+shift+h", - "smoke_screen_modal": "ctrl+alt+m", - "smoke_dialog_close": "escape,q" - } - } - ] - ] -} diff --git a/reference/opencode-1.15.5/.oxlintrc.json b/reference/opencode-1.15.5/.oxlintrc.json deleted file mode 100755 index f1ca1ff..0000000 --- a/reference/opencode-1.15.5/.oxlintrc.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/nicolo-ribaudo/oxc-project.github.io/refs/heads/json-schema/src/public/.oxlintrc.schema.json", - "options": { - "typeAware": true - }, - "categories": { - "suspicious": "warn" - }, - "rules": { - "typescript/no-base-to-string": "warn", - // Effect uses `function*` with Effect.gen/Effect.fnUntraced that don't always yield - "require-yield": "off", - // SolidJS uses `let ref: T | undefined` for JSX ref bindings assigned at runtime - "no-unassigned-vars": "off", - // SolidJS tracks reactive deps by reading properties inside createEffect - "no-unused-expressions": "off", - // Intentional control char matching (ANSI escapes, null byte sanitization) - "no-control-regex": "off", - // SST and plugin tools require triple-slash references - "triple-slash-reference": "off", - - // Suspicious category: suppress noisy rules - // Effect's nested function* closures inherently shadow outer scope - "no-shadow": "off", - // Namespace-heavy codebase makes this too noisy - "unicorn/consistent-function-scoping": "off", - // Opinionated — .sort()/.reverse() mutation is fine in this codebase - "unicorn/no-array-sort": "off", - "unicorn/no-array-reverse": "off", - // Not relevant — this isn't a DOM event handler codebase - "unicorn/prefer-add-event-listener": "off", - // Bundler handles module resolution - "unicorn/require-module-specifiers": "off", - // postMessage target origin not relevant for this codebase - "unicorn/require-post-message-target-origin": "off", - // Side-effectful constructors are intentional in some places - "no-new": "off", - - // Type-aware: catch unhandled promises - "typescript/no-floating-promises": "warn", - // Warn when spreading non-plain objects (Headers, class instances, etc.) - "typescript/no-misused-spread": "warn" - }, - "options": { - "typeAware": true - }, - "options": { - "typeAware": true - }, - "ignorePatterns": ["**/node_modules", "**/dist", "**/.build", "**/.sst", "**/*.d.ts", "**/sdk.gen.ts"] -} diff --git a/reference/opencode-1.15.5/.prettierignore b/reference/opencode-1.15.5/.prettierignore deleted file mode 100755 index a2a2776..0000000 --- a/reference/opencode-1.15.5/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -sst-env.d.ts -packages/desktop/src/bindings.ts diff --git a/reference/opencode-1.15.5/.zed/settings.json b/reference/opencode-1.15.5/.zed/settings.json deleted file mode 100755 index a3a5e1e..0000000 --- a/reference/opencode-1.15.5/.zed/settings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "format_on_save": "on", - "formatter": { - "external": { - "command": "bunx", - "arguments": ["prettier", "--stdin-filepath", "{buffer_path}"] - } - } -}