diff --git a/.github b/.github new file mode 120000 index 00000000000..f338b252e4c --- /dev/null +++ b/.github @@ -0,0 +1 @@ +/home/igorw/Work/howcode-config/.github \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml deleted file mode 100644 index fe1ec8409b4..00000000000 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Bug report -description: Report an issue that should be fixed -labels: ["bug"] -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/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 459ce25d05b..00000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: true -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/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml deleted file mode 100644 index 92e6c47570a..00000000000 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: 🚀 Feature Request -description: Suggest an idea, feature, or enhancement -labels: [discussion] -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/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml deleted file mode 100644 index 2310bfcc86b..00000000000 --- a/.github/ISSUE_TEMPLATE/question.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: Question -description: Ask a question -labels: ["question"] -body: - - type: textarea - id: question - attributes: - label: Question - description: What's your question? - validations: - required: true diff --git a/.github/actions/setup-bun/action.yml b/.github/actions/setup-bun/action.yml deleted file mode 100644 index cba04faccef..00000000000 --- a/.github/actions/setup-bun/action.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: "Setup Bun" -description: "Setup Bun with caching and install dependencies" -runs: - using: "composite" - steps: - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version-file: package.json - - - name: Cache ~/.bun - id: cache-bun - uses: actions/cache@v4 - with: - path: ~/.bun - key: ${{ runner.os }}-bun-${{ hashFiles('package.json') }}-${{ hashFiles('bun.lockb', 'bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun-${{ hashFiles('package.json') }}- - - - name: Install dependencies - run: bun install - shell: bash diff --git a/.github/publish-python-sdk.yml b/.github/publish-python-sdk.yml deleted file mode 100644 index 151ecb9944b..00000000000 --- a/.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/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 25466a63e06..00000000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: deploy - -on: - push: - branches: - - dev - - production - workflow_dispatch: - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -jobs: - deploy: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@v3 - - - uses: ./.github/actions/setup-bun - - - uses: actions/setup-node@v4 - with: - node-version: "24" - - - 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 }} diff --git a/.github/workflows/docs-update.yml b/.github/workflows/docs-update.yml deleted file mode 100644 index a8dd2ae4f2b..00000000000 --- a/.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@v4 - 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@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/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml deleted file mode 100644 index dc82d297bd1..00000000000 --- a/.github/workflows/duplicate-issues.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Duplicate Issue Detection - -on: - issues: - types: [opened] - -jobs: - check-duplicates: - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - issues: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - uses: ./.github/actions/setup-bun - - - name: Install opencode - run: curl -fsSL https://opencode.ai/install | bash - - - name: Check for duplicate issues - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: | - { - "bash": { - "gh issue*": "allow", - "*": "deny" - }, - "webfetch": "deny" - } - run: | - opencode run -m opencode/claude-haiku-4-5 "A new issue has been created:' - - Issue number: - ${{ github.event.issue.number }} - - Lookup this issue and search through existing issues (excluding #${{ github.event.issue.number }}) in this repository to find any potential duplicates of this new issue. - Consider: - 1. Similar titles or descriptions - 2. Same error messages or symptoms - 3. Related functionality or components - 4. Similar feature requests - - If you find any potential duplicates, please comment on the new issue with: - - A brief explanation of why it might be a duplicate - - Links to the potentially duplicate issues - - A suggestion to check those issues first - - Use this format for the comment: - 'This issue might be a duplicate of existing issues. Please check: - - #[issue_number]: [brief description of similarity] - - Feel free to ignore if none of these address your specific case.' - - Additionally, if the issue mentions keybinds, keyboard shortcuts, or key bindings, please add a comment mentioning the pinned keybinds issue #4997: - 'For keybind-related issues, please also check our pinned keybinds documentation: #4997' - - If no clear duplicates are found, do not comment." diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml deleted file mode 100644 index 29cc9895393..00000000000 --- a/.github/workflows/generate.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: generate - -on: - push: - branches: - - dev - workflow_dispatch: - -jobs: - generate: - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: write - pull-requests: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} - ref: ${{ github.event.pull_request.head.ref || github.ref_name }} - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - 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 config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add -A - git commit -m "chore: generate" - 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/.github/workflows/notify-discord.yml b/.github/workflows/notify-discord.yml deleted file mode 100644 index 62577ecf00e..00000000000 --- a/.github/workflows/notify-discord.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: 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@v1 - with: - webhook_url: ${{ secrets.DISCORD_WEBHOOK }} diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml deleted file mode 100644 index a8a02185e43..00000000000 --- a/.github/workflows/opencode.yml +++ /dev/null @@ -1,32 +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: ubuntu-latest - permissions: - id-token: write - contents: write - pull-requests: write - issues: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Run opencode - uses: anomalyco/opencode/github@latest - env: - ZHIPU_API_KEY: ${{ secrets.ZHIPU_API_KEY }} - OPENCODE_PERMISSION: '{"bash": "deny"}' - with: - model: zai-coding-plan/glm-4.7 diff --git a/.github/workflows/publish-github-action.yml b/.github/workflows/publish-github-action.yml deleted file mode 100644 index d2789373a34..00000000000 --- a/.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@v3 - 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/.github/workflows/publish-vscode.yml b/.github/workflows/publish-vscode.yml deleted file mode 100644 index f49a1057807..00000000000 --- a/.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@v3 - 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/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 5720996170e..00000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,233 +0,0 @@ -name: publish -run-name: "${{ format('release {0}', inputs.bump) }}" - -on: - push: - branches: - - dev - - 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: - publish: - runs-on: blacksmith-4vcpu-ubuntu-2404 - if: github.repository == 'anomalyco/opencode' - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - run: git fetch --force --tags - - - uses: ./.github/actions/setup-bun - - - name: Install OpenCode - if: inputs.bump || inputs.version - run: bun i -g opencode-ai@1.0.169 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - uses: actions/setup-node@v4 - with: - node-version: "24" - registry-url: "https://registry.npmjs.org" - - - name: Setup Git Identity - run: | - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" - git remote set-url origin https://x-access-token:${{ secrets.SST_GITHUB_TOKEN }}@github.com/${{ github.repository }} - - - name: Publish - id: publish - run: ./script/publish-start.ts - env: - OPENCODE_BUMP: ${{ inputs.bump }} - OPENCODE_VERSION: ${{ inputs.version }} - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - AUR_KEY: ${{ secrets.AUR_KEY }} - GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} - NPM_CONFIG_PROVENANCE: false - - - uses: actions/upload-artifact@v4 - with: - name: opencode-cli - path: packages/opencode/dist - - outputs: - release: ${{ steps.publish.outputs.release }} - tag: ${{ steps.publish.outputs.tag }} - version: ${{ steps.publish.outputs.version }} - - publish-tauri: - needs: publish - continue-on-error: true - strategy: - fail-fast: false - matrix: - settings: - - host: macos-latest - target: x86_64-apple-darwin - - host: macos-latest - target: aarch64-apple-darwin - - host: blacksmith-4vcpu-windows-2025 - target: x86_64-pc-windows-msvc - - host: blacksmith-4vcpu-ubuntu-2404 - target: x86_64-unknown-linux-gnu - - host: blacksmith-4vcpu-ubuntu-2404-arm - target: aarch64-unknown-linux-gnu - runs-on: ${{ matrix.settings.host }} - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ needs.publish.outputs.tag }} - - - uses: apple-actions/import-codesign-certs@v2 - if: ${{ runner.os == 'macOS' }} - with: - keychain: build - p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }} - p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - - - name: Verify Certificate - if: ${{ runner.os == 'macOS' }} - run: | - CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application") - CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}') - echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV - echo "Certificate imported." - - - name: Setup Apple API Key - if: ${{ runner.os == 'macOS' }} - run: | - echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8 - - - run: git fetch --force --tags - - - uses: ./.github/actions/setup-bun - - - name: install dependencies (ubuntu only) - if: contains(matrix.settings.host, 'ubuntu') - run: | - sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf - - - name: install Rust stable - uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ matrix.settings.target }} - - - uses: Swatinem/rust-cache@v2 - with: - workspaces: packages/desktop/src-tauri - shared-key: ${{ matrix.settings.target }} - - - name: Prepare - run: | - cd packages/desktop - bun ./scripts/prepare.ts - env: - OPENCODE_VERSION: ${{ needs.publish.outputs.version }} - NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} - AUR_KEY: ${{ secrets.AUR_KEY }} - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - RUST_TARGET: ${{ matrix.settings.target }} - GH_TOKEN: ${{ github.token }} - GITHUB_RUN_ID: ${{ github.run_id }} - - # Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released - - name: Install tauri-cli from portable appimage branch - if: contains(matrix.settings.host, 'ubuntu') - run: | - cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force - echo "Installed tauri-cli version:" - cargo tauri --version - - - name: Build and upload artifacts - timeout-minutes: 20 - uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: true - TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} - TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} - APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} - APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }} - APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} - APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} - APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8 - with: - projectPath: packages/desktop - uploadWorkflowArtifacts: true - tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }} - args: --target ${{ matrix.settings.target }} --config ./src-tauri/tauri.prod.conf.json --verbose - updaterJsonPreferNsis: true - releaseId: ${{ needs.publish.outputs.release }} - tagName: ${{ needs.publish.outputs.tag }} - releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext] - releaseDraft: true - - publish-release: - needs: - - publish - - publish-tauri - if: needs.publish.outputs.tag - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ needs.publish.outputs.tag }} - - - uses: ./.github/actions/setup-bun - - - 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-complete.ts - env: - OPENCODE_VERSION: ${{ needs.publish.outputs.version }} - AUR_KEY: ${{ secrets.AUR_KEY }} - GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} diff --git a/.github/workflows/release-github-action.yml b/.github/workflows/release-github-action.yml deleted file mode 100644 index 3f5caa55c8d..00000000000 --- a/.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@v4 - 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/.github/workflows/review.yml b/.github/workflows/review.yml deleted file mode 100644 index 44bfeb33661..00000000000 --- a/.github/workflows/review.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Guidelines Check - -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@v4 - 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: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "gh pr review*": "deny", "*": "deny" } }' - PR_TITLE: ${{ steps.pr-details.outputs.title }} - run: | - PR_BODY=$(jq -r .body pr_data.json) - opencode run -m anthropic/claude-opus-4-5 "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/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml deleted file mode 100644 index b5378d7d527..00000000000 --- a/.github/workflows/stale-issues.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: "Auto-close stale issues" - -on: - schedule: - - cron: "30 1 * * *" # Daily at 1:30 AM - workflow_dispatch: - -env: - DAYS_BEFORE_STALE: 90 - DAYS_BEFORE_CLOSE: 7 - -jobs: - stale: - runs-on: ubuntu-latest - permissions: - issues: write - steps: - - uses: actions/stale@v10 - with: - days-before-stale: ${{ env.DAYS_BEFORE_STALE }} - days-before-close: ${{ env.DAYS_BEFORE_CLOSE }} - stale-issue-label: "stale" - close-issue-message: | - [automated] Closing due to ${{ env.DAYS_BEFORE_STALE }}+ days of inactivity. - - Feel free to reopen if you still need this! - stale-issue-message: | - [automated] This issue has had no activity for ${{ env.DAYS_BEFORE_STALE }} days. - - It will be closed in ${{ env.DAYS_BEFORE_CLOSE }} days if there's no new activity. - remove-stale-when-updated: true - exempt-issue-labels: "pinned,security,feature-request,on-hold" - start-date: "2025-12-27" diff --git a/.github/workflows/stats.yml b/.github/workflows/stats.yml deleted file mode 100644 index 57e93642b27..00000000000 --- a/.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 == 'sst/opencode' - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - 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/.github/workflows/sync-zed-extension.yml b/.github/workflows/sync-zed-extension.yml deleted file mode 100644 index f14487cde97..00000000000 --- a/.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@v4 - 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/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index c39710bee8f..00000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: test - -on: - push: - branches: - - dev - pull_request: - workflow_dispatch: -jobs: - test: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: run - run: | - git config --global user.email "bot@opencode.ai" - git config --global user.name "opencode" - bun turbo typecheck - bun turbo test - env: - CI: true diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml deleted file mode 100644 index 6e150957291..00000000000 --- a/.github/workflows/triage.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Issue 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@v4 - 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/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml deleted file mode 100644 index 011e23f5f6f..00000000000 --- a/.github/workflows/typecheck.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: typecheck - -on: - pull_request: - branches: [dev] - workflow_dispatch: - -jobs: - typecheck: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Run typecheck - run: bun typecheck diff --git a/.github/workflows/update-nix-hashes.yml b/.github/workflows/update-nix-hashes.yml deleted file mode 100644 index d2c60b08f01..00000000000 --- a/.github/workflows/update-nix-hashes.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Update Nix Hashes - -permissions: - contents: write - -on: - workflow_dispatch: - push: - paths: - - "bun.lock" - - "package.json" - - "packages/*/package.json" - pull_request: - paths: - - "bun.lock" - - "package.json" - - "packages/*/package.json" - -jobs: - update: - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository - runs-on: blacksmith-4vcpu-ubuntu-2404 - env: - SYSTEM: x86_64-linux - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 - ref: ${{ github.head_ref || github.ref_name }} - repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} - - - name: Setup Nix - uses: DeterminateSystems/nix-installer-action@v20 - - - name: Configure git - run: | - git config --global user.email "action@github.com" - git config --global user.name "Github Action" - - - name: Update flake.lock - run: | - set -euo pipefail - echo "📦 Updating flake.lock..." - nix flake update - echo "✅ flake.lock updated successfully" - - - name: Update node_modules hash - run: | - set -euo pipefail - echo "🔄 Updating node_modules hash..." - nix/scripts/update-hashes.sh - echo "✅ node_modules hash updated successfully" - - - name: Commit hash changes - env: - TARGET_BRANCH: ${{ github.head_ref || github.ref_name }} - run: | - set -euo pipefail - - echo "🔍 Checking for changes in tracked Nix files..." - - summarize() { - local status="$1" - { - echo "### Nix Hash Update" - echo "" - echo "- ref: ${GITHUB_REF_NAME}" - echo "- status: ${status}" - } >> "$GITHUB_STEP_SUMMARY" - if [ -n "${GITHUB_SERVER_URL:-}" ] && [ -n "${GITHUB_REPOSITORY:-}" ] && [ -n "${GITHUB_RUN_ID:-}" ]; then - echo "- run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" >> "$GITHUB_STEP_SUMMARY" - fi - echo "" >> "$GITHUB_STEP_SUMMARY" - } - - FILES=(flake.lock flake.nix nix/node-modules.nix nix/hashes.json) - STATUS="$(git status --short -- "${FILES[@]}" || true)" - if [ -z "$STATUS" ]; then - echo "✅ No changes detected. Hashes are already up to date." - summarize "no changes" - exit 0 - fi - - echo "📝 Changes detected:" - echo "$STATUS" - echo "🔗 Staging files..." - git add "${FILES[@]}" - echo "💾 Committing changes..." - git commit -m "Update Nix flake.lock and hashes" - echo "✅ Changes committed" - - BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}" - echo "🌳 Pulling latest from branch: $BRANCH" - git pull --rebase origin "$BRANCH" - echo "🚀 Pushing changes to branch: $BRANCH" - git push origin HEAD:"$BRANCH" - echo "✅ Changes pushed successfully" - - summarize "committed $(git rev-parse --short HEAD)" diff --git a/.gitignore b/.gitignore index fc175568df6..c2753d9f606 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ target # Local dev files opencode-dev logs/ +.opencode/ diff --git a/.opencode b/.opencode new file mode 120000 index 00000000000..2dc838bc326 --- /dev/null +++ b/.opencode @@ -0,0 +1 @@ +/home/igorw/Work/howcode-config/.opencode \ No newline at end of file diff --git a/.opencode/agent/docs.md b/.opencode/agent/docs.md deleted file mode 100644 index 21cfc6a16e0..00000000000 --- a/.opencode/agent/docs.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: ALWAYS use this when writing docs -color: "#38A3EE" ---- - -You are an expert technical documentation writer - -You are not verbose - -Use a relaxed and friendly tone - -The title of the page should be a word or a 2-3 word phrase - -The description should be one short line, should not start with "The", should -avoid repeating the title of the page, should be 5-10 words long - -Chunks of text should not be more than 2 sentences long - -Each section is separated by a divider of 3 dashes - -The section titles are short with only the first letter of the word capitalized - -The section titles are in the imperative mood - -The section titles should not repeat the term used in the page title, for -example, if the page title is "Models", avoid using a section title like "Add -new models". This might be unavoidable in some cases, but try to avoid it. - -Check out the /packages/web/src/content/docs/docs/index.mdx as an example. - -For JS or TS code snippets remove trailing semicolons and any trailing commas -that might not be needed. - -If you are making a commit prefix the commit message with `docs:` diff --git a/.opencode/agent/triage.md b/.opencode/agent/triage.md deleted file mode 100644 index 539be154917..00000000000 --- a/.opencode/agent/triage.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -mode: primary -hidden: true -model: opencode/claude-haiku-4-5 -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. - -## Labels - -### windows - -Use for any issue that mentions Windows (the OS). Be sure they are saying that they are on Windows. - -- Use if they mention WSL too - -#### perf - -Performance-related issues: - -- Slow performance -- High RAM usage -- High CPU usage - -**Only** add if it's likely a RAM or CPU issue. **Do not** add for LLM slowness. - -#### desktop - -Desktop app issues: - -- `opencode web` command -- The desktop app itself - -**Only** add if it's specifically about the Desktop application or `opencode web` view. **Do not** add for terminal, TUI, or general opencode issues. - -#### nix - -**Only** add if the issue explicitly mentions nix. - -#### zen - -**Only** add if the issue mentions "zen" or "opencode zen". Zen is our gateway for coding models. **Do not** add for other gateways or inference providers. - -If the issue doesn't have "zen" in it then don't add zen label - -#### docs - -Add if the issue requests better documentation or docs updates. - -#### opentui - -TUI issues potentially caused by our underlying TUI library: - -- Keybindings not working -- Scroll speed issues (too fast/slow/laggy) -- Screen flickering -- Crashes with opentui in the log - -**Do not** add for general TUI bugs. - -When assigning to people here are the following rules: - -adamdotdev: -ONLY assign adam if the issue will have the "desktop" label. - -fwang: -ONLY assign fwang if the issue will have the "zen" label. - -jayair: -ONLY assign jayair if the issue will have the "docs" label. - -In all other cases use best judgment. Avoid assigning to kommander needlessly, when in doubt assign to rekram1-node. diff --git a/.opencode/command/commit.md b/.opencode/command/commit.md deleted file mode 100644 index 8e9346ebc88..00000000000 --- a/.opencode/command/commit.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -description: git commit and push -model: opencode/glm-4.6 -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. - -For anything in the packages/app use the ignore: 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 changes do a git pull --rebase -if there are conflicts DO NOT FIX THEM. notify me and I will fix them diff --git a/.opencode/command/issues.md b/.opencode/command/issues.md deleted file mode 100644 index 20ac4c18024..00000000000 --- a/.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 sst/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/.opencode/command/rmslop.md b/.opencode/command/rmslop.md deleted file mode 100644 index 02c9fc0844a..00000000000 --- a/.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/.opencode/command/spellcheck.md b/.opencode/command/spellcheck.md deleted file mode 100644 index 0abf23c4fd0..00000000000 --- a/.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/.opencode/env.d.ts b/.opencode/env.d.ts deleted file mode 100644 index f2b13a934c4..00000000000 --- a/.opencode/env.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module "*.txt" { - const content: string - export default content -} diff --git a/.opencode/opencode.jsonc b/.opencode/opencode.jsonc deleted file mode 100644 index 6008ab9bc0c..00000000000 --- a/.opencode/opencode.jsonc +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "https://opencode.ai/config.json", - // "plugin": ["opencode-openai-codex-auth"], - // "enterprise": { - // "url": "https://enterprise.dev.opencode.ai", - // }, - "instructions": ["STYLE_GUIDE.md"], - "provider": { - "opencode": { - "options": {}, - }, - }, - "permission": { - "bash": { - "ls foo": "ask", - }, - }, - "mcp": { - "context7": { - "type": "remote", - "url": "https://mcp.context7.com/mcp", - }, - }, - "tools": { - "github-triage": false, - }, -} diff --git a/.opencode/skill/test-skill/SKILL.md b/.opencode/skill/test-skill/SKILL.md deleted file mode 100644 index 3fef059f2e9..00000000000 --- a/.opencode/skill/test-skill/SKILL.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -name: test-skill -description: use this when asked to test skill ---- - -woah this is a test skill diff --git a/.opencode/themes/mytheme.json b/.opencode/themes/mytheme.json deleted file mode 100644 index e444de807c6..00000000000 --- a/.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": "nord2", - "light": "nord4" - }, - "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/.opencode/tool/github-triage.ts b/.opencode/tool/github-triage.ts deleted file mode 100644 index a5e6c811d83..00000000000 --- a/.opencode/tool/github-triage.ts +++ /dev/null @@ -1,90 +0,0 @@ -/// -// import { Octokit } from "@octokit/rest" -import { tool } from "@opencode-ai/plugin" -import DESCRIPTION from "./github-triage.txt" - -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, - }, - }) - if (!response.ok) { - throw new Error(`GitHub API error: ${response.status} ${response.statusText}`) - } - return response.json() -} - -export default tool({ - description: DESCRIPTION, - args: { - assignee: tool.schema - .enum(["thdxr", "adamdotdevin", "rekram1-node", "fwang", "jayair", "kommander"]) - .describe("The username of the assignee") - .default("rekram1-node"), - labels: tool.schema - .array(tool.schema.enum(["nix", "opentui", "perf", "desktop", "zen", "docs", "windows"])) - .describe("The labels(s) to add to the issue") - .default([]), - }, - async execute(args) { - const issue = getIssueNumber() - // const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }) - const owner = "sst" - const repo = "opencode" - - const results: string[] = [] - - if (args.assignee === "adamdotdevin" && !args.labels.includes("desktop")) { - throw new Error("Only desktop issues should be assigned to adamdotdevin") - } - - if (args.assignee === "fwang" && !args.labels.includes("zen")) { - throw new Error("Only zen issues should be assigned to fwang") - } - - if (args.assignee === "kommander" && !args.labels.includes("opentui")) { - throw new Error("Only opentui issues should be assigned to kommander") - } - - // await octokit.rest.issues.addAssignees({ - // owner, - // repo, - // issue_number: issue, - // assignees: [args.assignee], - // }) - await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, { - method: "POST", - body: JSON.stringify({ assignees: [args.assignee] }), - }) - results.push(`Assigned @${args.assignee} to issue #${issue}`) - - const labels: string[] = args.labels.map((label) => (label === "desktop" ? "web" : label)) - - if (labels.length > 0) { - // await octokit.rest.issues.addLabels({ - // owner, - // repo, - // issue_number: issue, - // labels, - // }) - await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/labels`, { - method: "POST", - body: JSON.stringify({ labels }), - }) - results.push(`Added labels: ${args.labels.join(", ")}`) - } - - return results.join("\n") - }, -}) diff --git a/.opencode/tool/github-triage.txt b/.opencode/tool/github-triage.txt deleted file mode 100644 index 4c46a72c162..00000000000 --- a/.opencode/tool/github-triage.txt +++ /dev/null @@ -1,88 +0,0 @@ -Use this tool to assign and/or label a Github issue. - -You can assign the following users: -- thdxr -- adamdotdevin -- fwang -- jayair -- kommander -- rekram1-node - - -You can use the following labels: -- nix -- opentui -- perf -- web -- zen -- docs - -Always try to assign an issue, if in doubt, assign rekram1-node to it. - -## Breakdown of responsibilities: - -### thdxr - -Dax is responsible for managing core parts of the application, for large feature requests, api changes, or things that require significant changes to the codebase assign him. - -This relates to OpenCode server primarily but has overlap with just about anything - -### adamdotdevin - -Adam is responsible for managing the Desktop/Web app. If there is an issue relating to the desktop app or `opencode web` command. Assign him. - - -### fwang - -Frank is responsible for managing Zen, if you see complaints about OpenCode Zen, maybe it's the dashboard, the model quality, billing issues, etc. Assign him to the issue. - -### jayair - -Jay is responsible for documentation. If there is an issue relating to documentation assign him. - -### kommander - -Sebastian is responsible for managing an OpenTUI (a library for building terminal user interfaces). OpenCode's TUI is built with OpenTUI. If there are issues about: -- random characters on screen -- keybinds not working on different terminals -- general terminal stuff -Then assign the issue to Him. - -### rekram1-node - -ALL BUGS SHOULD BE assigned to rekram1-node unless they have the `opentui` label. - -Assign Aiden to an issue as a catch all, if you can't assign anyone else. Most of the time this will be bugs/polish things. -If no one else makes sense to assign, assign rekram1-node to it. - -Always assign to aiden if the issue mentions "acp", "zed", or model performance issues - -## Breakdown of Labels: - -### nix - -Any issue that mentions nix, or nixos should have a nix label - -### opentui - -Anything relating to the TUI itself should have an opentui label - -### perf - -Anything related to slow performance, high ram, high cpu usage, or any other performance related issue should have a perf label - -### desktop - -Anything related to `opencode web` command or the desktop app should have a desktop label. Never add this label for anything terminal/tui related - -### zen - -Anything related to OpenCode Zen, billing, or model quality from Zen should have a zen label - -### docs - -Anything related to the documentation should have a docs label - -### windows - -Use for any issue that involves the windows OS diff --git a/AGENTS.md b/AGENTS.md index 87d59d4c923..98fc0e44d45 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,66 @@ -- To test opencode in the `packages/opencode` directory you can run `bun dev` -- To regenerate the javascript SDK, run ./packages/sdk/js/script/build.ts -- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. -- the default branch in this repo is `dev` + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. + + +# Agent Development Guidelines + +This document defines the core standards and operational protocols for agents working on the Opencode codebase. + + +Opencode is a Bun-powered monorepo for AI-driven development. It utilizes a high-performance stack including TypeScript 5.8+, SolidJS, and SST. The architecture emphasizes erasable syntax, minimal runtime overhead, and strict type safety. + + + + +## 1. Commands & Operations + +- **Development**: MUST run `bun dev` to start the core Opencode application. +- **Testing**: + - Root tests are disabled. MUST run tests from within specific package directories (e.g., `cd packages/opencode && bun test`). + - To test Opencode specifically, use `bun dev` in `packages/opencode`. +- **Typechecking**: SHOULD use `bun run typecheck` (Turbo-powered) for workspace-wide validation. +- **SDK**: MUST regenerate the JavaScript SDK using `./packages/sdk/js/script/build.ts` after API changes. +- **Git**: The default branch is `dev`. +- **Fork Syncing**: + - Agents MUST stash the `.opencode` folder before syncing with upstream. + - Agents MUST NOT sync the `.opencode` folder from upstream. + - Ensure NO EXTRA FILES (untracked or new upstream files) are accidentally added during sync. + +## 2. Coding Standards + +### Structure & Organization + +- **File Headers**: Every file MUST start with a 2-3 sentence comment explaining its purpose. +- **File Size**: Files SHOULD NOT exceed 200 lines. Files over 300 lines MUST be split. +- **Function Size**: Functions SHOULD NOT exceed 40 lines. +- **Barrels**: Every module directory MUST have an `index.ts` file. Consumers MUST import from the barrel, not deep paths. +- **Domain Separation**: Organize code by business domain, not technical layers. + +### Style & Logic + +- **Minimalism**: Avoid `try/catch` and `else` statements where possible. Prefer "Fail Fast" patterns. +- **Variables**: PREFER single-word variable names. AVOID `let` statements and unnecessary destructuring. +- **Logging**: MUST NOT use `console.log`, `console.error`, or other logging. Trust the type system. +- **DRY**: Pattern recognition is mandatory. Abstract similar code immediately into shared utilities. + +### Type System + +- **Strictness**: MUST NOT use `any`. Use `unknown` and narrow via control flow. +- **Types vs Interfaces**: Prefer `type` for data shapes; use `interface` only for extensible public APIs. +- **Erasable Syntax**: MUST NOT use `enum`, `namespace`, or constructor parameter properties. +- **Imports**: MUST use `import type` for type-only imports and MUST include `.js` extensions in Node.js compatible imports. + +## 3. Tool Usage & AI Protocols + +- **Parallelism**: Agents MUST use parallel tool calls whenever multiple independent operations are possible. +- **Verification**: Agents MUST verify code changes by running relevant tests or build commands before reporting completion. +- **XML Tags**: Documentation and instructions SHOULD use XML tags (e.g., ``, ``) for clarity as per `RFC-XML-STYLE-GUIDE.md`. + + + + +1. **Explore**: Read relevant barrel files and existing implementations before writing new code. +2. **Implement**: Write code following the "Fail Fast" and "No Else" rules. +3. **Validate**: Run package-specific tests and typechecks. +4. **Clean**: Remove any temporary files or debug statements before submission. + diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index 913e54d1065..37469788928 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -108,6 +108,14 @@ function createGlobalSync() { return children[directory] } + function createClient(directory: string) { + return createOpencodeClient({ + baseUrl: globalSDK.url, + directory, + throwOnError: true, + }) + } + async function loadSessions(directory: string) { const [store, setStore] = child(directory) globalSDK.client.session @@ -137,11 +145,7 @@ function createGlobalSync() { async function bootstrapInstance(directory: string) { if (!directory) return const [store, setStore] = child(directory) - const sdk = createOpencodeClient({ - baseUrl: globalSDK.url, - directory, - throwOnError: true, - }) + const sdk = createClient(directory) const blockingRequests = { project: () => sdk.project.current().then((x) => setStore("project", x.data!.id)), @@ -394,11 +398,7 @@ function createGlobalSync() { break } case "lsp.updated": { - const sdk = createOpencodeClient({ - baseUrl: globalSDK.url, - directory, - throwOnError: true, - }) + const sdk = createClient(directory) sdk.lsp.status().then((x) => setStore("lsp", x.data ?? [])) break } diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 006d04f1cb3..481d036f09d 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -558,7 +558,11 @@ function App() { sdk.event.on(SessionApi.Event.Deleted.type, (evt) => { if (route.data.type === "session" && route.data.sessionID === evt.properties.info.id) { - route.navigate({ type: "home" }) + if (evt.properties.info.parentID) { + route.navigate({ type: "session", sessionID: evt.properties.info.parentID }) + } else { + route.navigate({ type: "home" }) + } toast.show({ variant: "info", message: "The current session was deleted", diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index 2e68fdcd924..be2bff2b89c 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -258,6 +258,9 @@ export function Autocomplete(props: { const results: AutocompleteOption[] = [] const s = session() for (const command of sync.data.command) { + if (command.name === "finished") continue + if (command.name === "wipe") continue + if (command.name === "wipeout") continue results.push({ display: "/" + command.name + (command.mcp ? " (MCP)" : ""), description: command.description, @@ -271,6 +274,22 @@ export function Autocomplete(props: { }) } if (s) { + if (s.parentID) { + results.push({ + display: "/finished", + description: "summarize work and return to parent session", + onSelect: () => command.trigger("session.finished"), + }) + + const status = sync.data.session_status?.[s.id] ?? { type: "idle" } + if (status.type === "idle") { + results.push({ + display: "/wipe", + description: "terminate and remove this subagent session", + onSelect: () => command.trigger("session.wipe"), + }) + } + } results.push( { display: "/undo", @@ -327,6 +346,21 @@ export function Autocomplete(props: { onSelect: () => command.trigger("session.toggle.thinking"), }, ) + + const idleChildren = sync.data.session.filter((child) => { + if (child.parentID !== s.id) return false + const status = sync.data.session_status?.[child.id] ?? { type: "idle" } + return status.type === "idle" + }) + + if (idleChildren.length > 0) { + results.push({ + display: "/wipeout", + description: `terminate ${idleChildren.length} idle subagent session${idleChildren.length > 1 ? "s" : ""}`, + onSelect: () => command.trigger("session.wipeout"), + }) + } + if (sync.data.config.share !== "disabled") { results.push({ display: "/share", diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index d049ec4373c..feec7dafd33 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -69,6 +69,7 @@ import { Filesystem } from "@/util/filesystem" import { PermissionPrompt } from "./permission" import { DialogExportOptions } from "../../ui/dialog-export-options" import { formatTranscript } from "../../util/transcript" +import { Identifier } from "@/id/id" addDefaultParsers(parsers.parsers) @@ -357,6 +358,108 @@ export function Session() { dialog.clear() }, }, + { + title: "Finish subtask", + value: "session.finished", + category: "Session", + onSelect: (dialog) => { + const selectedModel = local.model.current() + if (!selectedModel) { + toast.show({ + variant: "warning", + message: "Connect a provider to finish this session", + duration: 3000, + }) + return + } + sdk.client.session.command({ + sessionID: route.sessionID, + command: "finished", + arguments: "", + agent: local.agent.current().name, + model: `${selectedModel.providerID}/${selectedModel.modelID}`, + messageID: Identifier.ascending("message"), + variant: local.model.variant.current(), + }) + dialog.clear() + }, + }, + { + title: "Wipe session", + value: "session.wipe", + category: "Session", + onSelect: async (dialog) => { + const s = sync.session.get(route.sessionID) + if (!s?.parentID) return + + const sessionID = s.id + const parentID = s.parentID + + // Navigate BEFORE delete to avoid race with session.deleted event handler + dialog.clear() + navigate({ type: "session", sessionID: parentID }) + toast.show({ + message: "Session wiped", + variant: "success", + }) + + await sdk.client.session.command({ + sessionID, + command: "wipe", + arguments: "", + agent: local.agent.current().name, + model: "", + messageID: Identifier.ascending("message"), + variant: local.model.variant.current(), + }) + }, + }, + { + title: "Wipeout all subagents", + value: "session.wipeout", + category: "Session", + onSelect: async (dialog) => { + const children = await sdk.client.session.children({ sessionID: route.sessionID }) + const idle = (children.data ?? []).filter((c) => { + const status = sync.data.session_status?.[c.id] ?? { type: "idle" } + return status.type === "idle" + }) + + if (idle.length === 0) { + toast.show({ + variant: "info", + message: "No idle subagent sessions to wipe", + duration: 3000, + }) + return + } + + const confirmed = await DialogConfirm.show( + dialog, + "Wipeout All Subagents", + `This will permanently delete ${idle.length} idle subagent session${idle.length > 1 ? "s" : ""}. Continue?`, + ) + + if (!confirmed) return + + await sdk.client.session.command({ + sessionID: route.sessionID, + command: "wipeout", + arguments: "", + agent: local.agent.current().name, + model: "", + messageID: Identifier.ascending("message"), + variant: local.model.variant.current(), + }) + + toast.show({ + message: `Wiped ${idle.length} session${idle.length > 1 ? "s" : ""}`, + variant: "success", + }) + + dialog.clear() + }, + }, { title: "Unshare session", value: "session.unshare", @@ -1025,7 +1128,7 @@ export function Session() { { prompt = r promptRef.set(r) @@ -1620,19 +1723,34 @@ function Task(props: ToolProps) { const { theme } = useTheme() const keybind = useKeybind() const { navigate } = useRoute() + const sync = useSync() + const toast = useToast() const current = createMemo(() => props.metadata.summary?.findLast((x) => x.state.status !== "pending")) + const handleClick = () => { + const sessionId = props.metadata.sessionId + if (!sessionId) return + + const exists = sync.session.get(sessionId) + if (!exists) { + toast.show({ + variant: "info", + message: "Session was wiped", + duration: 3000, + }) + return + } + + navigate({ type: "session", sessionID: sessionId }) + } + return ( navigate({ type: "session", sessionID: props.metadata.sessionId! }) - : undefined - } + onClick={props.metadata.sessionId ? handleClick : undefined} part={props.part} > diff --git a/packages/opencode/src/command/index.ts b/packages/opencode/src/command/index.ts index 976f1cd51e9..9720f5254f1 100644 --- a/packages/opencode/src/command/index.ts +++ b/packages/opencode/src/command/index.ts @@ -53,6 +53,9 @@ export namespace Command { export const Default = { INIT: "init", REVIEW: "review", + FINISHED: "finished", + WIPE: "wipe", + WIPEOUT: "wipeout", } as const const state = Instance.state(async () => { @@ -76,6 +79,24 @@ export namespace Command { subtask: true, hints: hints(PROMPT_REVIEW), }, + [Default.FINISHED]: { + name: Default.FINISHED, + description: "summarize work and return to parent session", + template: "Summarize the work done in this session so far and finish the task.", + hints: [], + }, + [Default.WIPE]: { + name: Default.WIPE, + description: "terminate and remove this subagent session", + template: "", + hints: [], + }, + [Default.WIPEOUT]: { + name: Default.WIPEOUT, + description: "terminate all idle subagent sessions", + template: "", + hints: [], + }, } for (const [name, command] of Object.entries(cfg.command ?? {})) { diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 634539ade41..e2c52bb366c 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1363,6 +1363,37 @@ export namespace SessionPrompt { export async function command(input: CommandInput) { log.info("command", input) + + // Handle /wipe - early return, no prompt + if (input.command === "wipe") { + const session = await Session.get(input.sessionID) + + if (!session.parentID) { + throw new Error("Cannot wipe primary sessions") + } + + const status = SessionStatus.get(input.sessionID) + if (status.type !== "idle") { + throw new Error("Cannot wipe session while busy") + } + + await Session.remove(input.sessionID) + return + } + + // Handle /wipeout - early return, no prompt + if (input.command === "wipeout") { + const children = await Session.children(input.sessionID) + const idle = children.filter((c) => { + const status = SessionStatus.get(c.id) + return status.type === "idle" + }) + + await Promise.allSettled(idle.map((child) => Session.remove(child.id))) + + return + } + const command = await Command.get(input.command) const agentName = command.agent ?? input.agent ?? (await Agent.defaultAgent()) @@ -1466,6 +1497,22 @@ export namespace SessionPrompt { variant: input.variant, })) as MessageV2.WithParts + if (input.command === "finished") { + const session = await Session.get(input.sessionID) + if (session.parentID) { + const text = result.parts.findLast((x) => x.type === "text")?.text ?? "" + await prompt({ + sessionID: session.parentID, + parts: [ + { + type: "text", + text: `Subtask finished with the following summary:\n\n${text}\n\nSession: ${session.id}`, + }, + ], + }) + } + } + Bus.publish(Command.Event.Executed, { name: input.command, sessionID: input.sessionID,