From e91c6ef9563ad41b4c9d3bc1ca69033a094ca0b4 Mon Sep 17 00:00:00 2001 From: jbdevprimary Date: Thu, 8 Jan 2026 06:13:06 +0000 Subject: [PATCH] chore(sync): [skip actions] synced local './' with remote 'sync-files/always-sync/global/' Synced from jbcom/control-center Run: https://github.com/jbcom/control-center/actions/runs/20807422644 [skip actions] --- .cursor/rules/00-fundamentals.mdc | 44 ---- .cursor/rules/01-pr-workflow.mdc | 57 ----- .cursor/rules/02-memory-bank.mdc | 58 ----- .cursor/rules/03-docs-branding.mdc | 132 ----------- .cursor/rules/ci.mdc | 97 -------- .cursor/rules/releases.mdc | 72 ------ .github/workflows/autoheal.yml | 142 ++++++++++++ .github/workflows/delegator.yml | 90 ++++++++ .github/workflows/review.yml | 95 ++++++++ .github/workflows/triage.yml | 350 +++++++++++++++++++++++++++++ 10 files changed, 677 insertions(+), 460 deletions(-) delete mode 100644 .cursor/rules/00-fundamentals.mdc delete mode 100644 .cursor/rules/01-pr-workflow.mdc delete mode 100644 .cursor/rules/02-memory-bank.mdc delete mode 100644 .cursor/rules/03-docs-branding.mdc delete mode 100644 .cursor/rules/ci.mdc delete mode 100644 .cursor/rules/releases.mdc create mode 100644 .github/workflows/autoheal.yml create mode 100644 .github/workflows/delegator.yml create mode 100644 .github/workflows/review.yml create mode 100644 .github/workflows/triage.yml diff --git a/.cursor/rules/00-fundamentals.mdc b/.cursor/rules/00-fundamentals.mdc deleted file mode 100644 index 62bf7871..00000000 --- a/.cursor/rules/00-fundamentals.mdc +++ /dev/null @@ -1,44 +0,0 @@ ---- -description: Fundamental rules for all jbcom repos -globs: "**/*" -alwaysApply: true ---- - -# Fundamentals - -## Before Acting - -1. **Read before modifying** - Always read the file/code before editing -2. **Run builds after changes** - Verify your changes compile/lint -3. **Check git status** - Know what you're committing - -## Making Changes - -1. **One concern per commit** - Keep commits focused -2. **Run linters/tests locally** before pushing -3. **Don't consolidate code** without verifying the build - -## When Copying/Moving Files - -1. Read the file first -2. Check if content is relevant to destination -3. Update paths and references -4. Remove tool-specific cruft that doesn't apply - -## Authentication - -```bash -# jbcom repos - ALWAYS use this pattern -GH_TOKEN="$GITHUB_TOKEN" gh -``` - -## Session Management - -```bash -# Start of session -cat memory-bank/activeContext.md 2>/dev/null || echo "No memory bank" -gh issue list --label agent-session 2>/dev/null || true - -# End of session - update context -echo "## Session: $(date +%Y-%m-%d)" >> memory-bank/activeContext.md -``` diff --git a/.cursor/rules/01-pr-workflow.mdc b/.cursor/rules/01-pr-workflow.mdc deleted file mode 100644 index 642e58da..00000000 --- a/.cursor/rules/01-pr-workflow.mdc +++ /dev/null @@ -1,57 +0,0 @@ ---- -description: PR creation and review workflow -globs: "**/*" ---- - -# PR Workflow - -## Creating PRs - -1. **One feature/fix per PR** - Keep scope minimal -2. **Clear title**: `type(scope): description` -3. **Body explains** WHAT, WHY, and HOW - -### Conventional Commit Types - -| Type | Use Case | -|------|----------| -| `feat` | New feature → minor bump | -| `fix` | Bug fix → patch bump | -| `docs` | Documentation only | -| `chore` | Maintenance tasks | -| `refactor` | Code restructuring | -| `test` | Adding tests | - -## AI Peer Review - -**ALWAYS request AI reviews on PRs:** - -``` -/gemini review -/q review -@copilot review -``` - -## Addressing Feedback - -1. Make commits that fix issues -2. Respond to EVERY comment (fix OR explain why not) -3. Re-request review after significant changes - -### Resolving Review Threads - -```bash -# Via GraphQL (preferred for batch resolution) -gh api graphql -f query='mutation { - resolveReviewThread(input: {threadId: "PRRT_xxx"}) { - thread { isResolved } - } -}' -``` - -## Merging - -- ✅ Wait for CI to pass -- ✅ Address all review comments -- ✅ Squash if commits are messy -- ❌ Never use `--admin` to bypass checks diff --git a/.cursor/rules/02-memory-bank.mdc b/.cursor/rules/02-memory-bank.mdc deleted file mode 100644 index d066c11e..00000000 --- a/.cursor/rules/02-memory-bank.mdc +++ /dev/null @@ -1,58 +0,0 @@ ---- -description: Session memory and handoff protocol -globs: "**/*" ---- - -# Memory Bank Protocol - -## Purpose - -Memory bank provides session continuity between agent runs. It's the "living memory" pattern. - -## Files - -| File | Purpose | -|------|---------| -| `memory-bank/activeContext.md` | Current state and focus | -| `memory-bank/progress.md` | Session-by-session log | - -## Session Start - -```bash -# Check context -if [ -f memory-bank/activeContext.md ]; then - cat memory-bank/activeContext.md -fi - -# Check for open issues -gh issue list --label agent-session 2>/dev/null || true -``` - -## Session End - -```bash -# Update context -cat >> memory-bank/activeContext.md << 'EOF' - -## Session: $(date +%Y-%m-%d) - -### Completed -- [x] Task 1 - -### For Next Agent -- [ ] Follow-up task -EOF - -# Commit if changes -git add memory-bank/ -git commit -m "docs: update memory bank for handoff" || true -``` - -## Handoff Notes - -When ending a session: - -1. **Summarize completed work** - What got done -2. **List outstanding tasks** - What's left -3. **Document blockers** - What stopped progress -4. **Include references** - PRs, issues, file paths diff --git a/.cursor/rules/03-docs-branding.mdc b/.cursor/rules/03-docs-branding.mdc deleted file mode 100644 index ae806c70..00000000 --- a/.cursor/rules/03-docs-branding.mdc +++ /dev/null @@ -1,132 +0,0 @@ ---- -description: Documentation branding and styling standards for jbcom ecosystem -globs: ["docs/**/*", "**/docs/**/*", "**/*.rst", "**/*.md"] ---- - -# Documentation Branding Standards - -## 🎨 jbcom Brand Requirements - -ALL documentation in jbcom repositories MUST follow the unified design system. - -### Reference Document - -See `docs/DESIGN-SYSTEM.md` for complete brand specifications. - -### Color Palette (Dark Theme) - -| Purpose | Hex | -|---------|-----| -| Background | `#0a0f1a` | -| Surface/Cards | `#111827` | -| Primary (Cyan) | `#06b6d4` | -| Secondary (Blue) | `#3b82f6` | -| Text Primary | `#f1f5f9` | -| Text Secondary | `#94a3b8` | -| Success | `#10b981` | -| Warning | `#f59e0b` | -| Error | `#ef4444` | - -### Typography - -| Element | Font | Weight | -|---------|------|--------| -| Headings | Space Grotesk | 600-700 | -| Body | Inter | 400-500 | -| Code | JetBrains Mono | 400 | - -## 📁 Language-Specific Scaffolds - -Use the appropriate scaffold from `repository-files/` for your language: - -| Language | Location | Tool | Files | -|----------|----------|------|-------| -| **Python** | `python/docs/` | Sphinx | `conf.py`, `jbcom-sphinx.css` | -| **TypeScript** | `nodejs/docs/` | TypeDoc | `typedoc.json`, `jbcom-typedoc.css` | -| **Rust** | `rust/docs/` | rustdoc | `README.md`, `jbcom-rustdoc.css` | -| **Go** | `go/docs/` | godoc | `README.md` (conventions) | -| **Terraform** | `terraform/docs/` | terraform-docs | `README.md`, config examples | - -## 📄 Python (Sphinx) - -Scaffold: `repository-files/python/docs/` - -```python -# conf.py - key settings -html_theme = "sphinx_rtd_theme" # or "furo" -html_static_path = ['_static'] -html_css_files = ['jbcom-sphinx.css'] -``` - -## 📘 TypeScript/Node.js (TypeDoc) - -Scaffold: `repository-files/nodejs/docs/` - -```json -// typedoc.json - key settings -{ - "customCss": "jbcom-typedoc.css", - "darkHighlightTheme": "github-dark" -} -``` - -## 🦀 Rust (rustdoc) - -Scaffold: `repository-files/rust/docs/` - -```bash -# Generate docs with jbcom branding -RUSTDOCFLAGS="--extend-css docs/jbcom-rustdoc.css" cargo doc --no-deps -``` - -## 🐹 Go (doc2go) - -Scaffold: `repository-files/go/docs/` - -```bash -# Install doc2go (godoc is deprecated) -go install go.abhg.dev/doc2go@latest - -# Generate with jbcom branding -doc2go -out docs/api ./... -cp docs/jbcom-doc2go.css docs/api/custom.css -``` - -See `docs/README.md` for full workflow including GitHub Pages deployment. - -## 🏗️ Terraform (terraform-docs) - -Scaffold: `repository-files/terraform/docs/` - -Use `.terraform-docs.yml` for auto-generated module documentation. - -## ✅ Documentation Checklist - -Before pushing documentation changes: - -- [ ] Uses jbcom color palette (dark theme) -- [ ] Headings use Space Grotesk font -- [ ] Body text uses Inter font -- [ ] Code blocks use JetBrains Mono -- [ ] Links are cyan (`#06b6d4`) -- [ ] Code blocks have proper contrast -- [ ] Admonitions follow semantic colors -- [ ] Focus states are visible (accessibility) -- [ ] Meets WCAG AA contrast requirements - -## 🚫 Do NOT - -- ❌ Use light theme as default -- ❌ Use different primary colors -- ❌ Use non-standard fonts without approval -- ❌ Create custom color schemes per-repo -- ❌ Ignore accessibility requirements - -## ✅ Do - -- ✅ Use dark theme consistently -- ✅ Use language-appropriate scaffold (Sphinx, TypeDoc, godoc, terraform-docs) -- ✅ Apply the provided jbcom CSS for your doc tool -- ✅ Follow the type scale specification -- ✅ Ensure sufficient color contrast -- ✅ Test with reduced motion preference diff --git a/.cursor/rules/ci.mdc b/.cursor/rules/ci.mdc deleted file mode 100644 index 8d0a6c6e..00000000 --- a/.cursor/rules/ci.mdc +++ /dev/null @@ -1,97 +0,0 @@ ---- -description: CI/CD workflow patterns -globs: "**/*" ---- - -# CI/CD Workflow - -## Branch Protection - -- **main**: Protected, requires PR and CI pass -- **Feature branches**: `feat/`, `fix/`, `docs/`, `chore/` - -## CI Checks - -Every PR must pass: - -1. **Build** - Package compiles/builds -2. **Test** - All tests pass -3. **Lint** - No linting errors -4. **Type check** - No type errors - -## Watching CI - -```bash -# List recent runs -gh run list --limit 5 - -# Watch a specific run -gh run watch - -# View run logs -gh run view --log -``` - -## Fixing CI Failures - -### Build Failures - -```bash -# Python -uv sync && uv run pytest - -# TypeScript -pnpm install && pnpm run build - -# Go -go mod download && go build ./... -``` - -### Lint Failures - -```bash -# Python -uvx ruff check --fix . -uvx ruff format . - -# TypeScript -pnpm run lint:fix - -# Go -golangci-lint run --fix -``` - -### Test Failures - -1. Read the failure output carefully -2. Reproduce locally -3. Fix the code or the test -4. Verify fix locally before pushing - -## Required Secrets - -These secrets power CI/CD: - -| Secret | Purpose | -|--------|---------| -| `CI_GITHUB_TOKEN` | GitHub API access | -| `PYPI_TOKEN` | PyPI publishing | -| `NPM_TOKEN` | npm publishing | -| `DOCKERHUB_USERNAME` | Docker Hub login | -| `DOCKERHUB_TOKEN` | Docker Hub publishing | - -## Troubleshooting - -### "Permission denied" - -Token lacks required scopes. Check: -- `repo` scope for repository access -- `workflow` scope for Actions -- `write:packages` for package publishing - -### "Rate limit exceeded" - -Use authenticated requests (gh CLI auto-uses GITHUB_TOKEN): -```bash -gh api ... -``` diff --git a/.cursor/rules/releases.mdc b/.cursor/rules/releases.mdc deleted file mode 100644 index 78e5a1af..00000000 --- a/.cursor/rules/releases.mdc +++ /dev/null @@ -1,72 +0,0 @@ ---- -description: Release and versioning workflow -globs: "**/*" ---- - -# Releases - -## Versioning - -All packages use **Semantic Versioning (SemVer)**: `MAJOR.MINOR.PATCH` - -- **MAJOR**: Breaking changes -- **MINOR**: New features (backward compatible) -- **PATCH**: Bug fixes (backward compatible) - -## Conventional Commits - -Commits drive automatic version bumps: - -``` -feat(scope): new feature → minor bump (x.Y.0) -fix(scope): bug fix → patch bump (x.y.Z) -feat(scope)!: breaking change → major bump (X.0.0) -``` - -### Package Scopes - -| Scope | Package | -|-------|---------| -| `edt` | extended-data-types | -| `logging` | lifecyclelogging | -| `dic` | directed-inputs-class | -| `bridge` | python-terraform-bridge | -| `connectors` | vendor-connectors | -| `agentic` | agentic-control | -| `vss` | vault-secret-sync | - -## Release Process - -``` -Push to main with conventional commit - ↓ -CI runs tests & lint - ↓ -Semantic release analyzes commits - ↓ -Version bumped automatically - ↓ -Package published (PyPI/npm/Docker) - ↓ -Synced to public repo -``` - -## What NOT to Do - -- ❌ **Never** manually edit `__version__` or `package.json` version -- ❌ **Never** create tags by hand -- ❌ **Never** skip CI for releases -- ❌ **Never** suggest alternative versioning schemes - -## Checking Release Status - -```bash -# Check if release will happen -semantic-release --noop version --print - -# Check recent releases -gh release list --limit 5 - -# Check CI status -gh run list --limit 3 -``` diff --git a/.github/workflows/autoheal.yml b/.github/workflows/autoheal.yml new file mode 100644 index 00000000..3d784f47 --- /dev/null +++ b/.github/workflows/autoheal.yml @@ -0,0 +1,142 @@ +name: Autoheal +# Consolidated CI failure resolution workflow +# Replaces: ai-fixer, ecosystem-fixer, ecosystem-fixer-local +# +# INTELLIGENT CONFLICT RESOLUTION: +# - Automatically resolves CI failures +# - Resolves merge conflicts triggered by triage workflow +# - Uses AI to analyze and fix issues + +on: + workflow_run: + workflows: ["CI", "Go"] + types: [completed] + workflow_call: + inputs: + operation: + description: 'Operation type (analyze_failure, resolve_conflicts)' + required: false + type: string + default: 'analyze_failure' + run_id: + description: 'Workflow run ID to fix' + required: false + type: string + pr_number: + description: 'PR number (for conflict resolution)' + required: false + type: string + repository: + description: 'Repository (owner/repo)' + required: true + type: string + workflow_dispatch: + inputs: + operation: + description: 'Operation to perform' + required: true + type: choice + options: + - analyze_failure + - resolve_conflicts + - apply_fix + run_id: + description: 'Workflow run ID to fix (for failures)' + required: false + type: string + pr_number: + description: 'PR number (for conflicts)' + required: false + type: string + repository: + description: 'Repository (owner/repo)' + required: false + type: string + +permissions: + contents: write + pull-requests: write + issues: write + actions: read + +jobs: + # ============================================ + # CI FAILURE RESOLUTION + # ============================================ + quick-fix: + runs-on: ubuntu-latest + if: | + (github.event.workflow_run.conclusion == 'failure') || + (github.event_name == 'workflow_call' && inputs.operation == 'analyze_failure') || + (github.event_name == 'workflow_dispatch' && inputs.operation == 'analyze_failure') + steps: + - name: Analyze failure with control-center + id: analyze + run: | + RUN_ID="${{ inputs.run_id || github.event.workflow_run.id }}" + REPO="${{ inputs.repository || github.repository }}" + + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + -e OLLAMA_API_KEY="${OLLAMA_API_KEY}" \ + jbcom/control-center:latest fixer analyze \ + --run-id "$RUN_ID" \ + --repo "$REPO" \ + --output json > fix-suggestion.json + + echo "suggestion<> $GITHUB_OUTPUT + cat fix-suggestion.json >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OLLAMA_API_KEY: ${{ secrets.OLLAMA_API_KEY }} + + - name: Apply quick fix + if: steps.analyze.outputs.suggestion != '' + run: | + docker run --rm \ + -v "${{ github.workspace }}:/workspace" \ + -w /workspace \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest fixer apply \ + --suggestion-file fix-suggestion.json \ + --repo ${{ inputs.repository || github.repository }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Comment fix on PR + if: github.event.workflow_run.pull_requests[0] + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const fs = require('fs'); + const suggestion = JSON.parse(fs.readFileSync('fix-suggestion.json', 'utf8')); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.workflow_run.pull_requests[0].number, + body: `## 🔧 CI Fix Suggestion\n\n${suggestion.description}\n\n---\n🤖 Generated by Control Center Autoheal` + }); + + # ============================================ + # MERGE CONFLICT RESOLUTION + # ============================================ + resolve-conflicts: + runs-on: ubuntu-latest + if: | + (github.event_name == 'workflow_call' && inputs.operation == 'resolve_conflicts') || + (github.event_name == 'workflow_dispatch' && inputs.operation == 'resolve_conflicts') + steps: + - name: Post conflict resolution guidance + run: | + PR_NUM="${{ inputs.pr_number }}" + REPO="${{ inputs.repository || github.repository }}" + + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest fixer resolve-conflict \ + --repo "$REPO" \ + --pr "$PR_NUM" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/delegator.yml b/.github/workflows/delegator.yml new file mode 100644 index 00000000..e5e47f6a --- /dev/null +++ b/.github/workflows/delegator.yml @@ -0,0 +1,90 @@ +name: Delegator +# Consolidated @claude command router +# Replaces: ai-delegator, ecosystem-delegator, ecosystem-delegator-local + +on: + issue_comment: + types: [created] + workflow_call: + inputs: + comment_id: + description: 'Comment ID containing @claude command' + required: true + type: string + repository: + description: 'Repository (owner/repo)' + required: true + type: string + workflow_dispatch: + inputs: + issue_number: + description: 'Issue/PR number' + required: true + type: string + command: + description: 'Command to execute' + required: true + type: string + repository: + description: 'Repository (owner/repo)' + required: false + type: string + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + delegate: + runs-on: ubuntu-latest + if: contains(github.event.comment.body, '@claude') || github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' + steps: + - name: Checkout + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.3.0 + with: + repository: ${{ inputs.repository || github.repository }} + + - name: Parse command + id: parse + env: + COMMENT_BODY: ${{ github.event.comment.body }} + run: | + if [ "${{ github.event_name }}" = "issue_comment" ]; then + COMMAND=$(echo "$COMMENT_BODY" | grep -oP '@claude\s+\K.*' || echo "") + ISSUE_NUM="${{ github.event.issue.number }}" + elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + COMMAND="${{ inputs.command }}" + ISSUE_NUM="${{ inputs.issue_number }}" + else + COMMAND="" + ISSUE_NUM="" + fi + + echo "command=$COMMAND" >> $GITHUB_OUTPUT + echo "issue_number=$ISSUE_NUM" >> $GITHUB_OUTPUT + + - name: Execute command + if: steps.parse.outputs.command != '' + run: | + echo "Delegating command: ${{ steps.parse.outputs.command }}" + echo "Issue/PR: ${{ steps.parse.outputs.issue_number }}" + + # In a real implementation, this would call Claude API + # or control-center with Claude integration + echo "Command execution delegated to Claude agent" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + + - name: Acknowledge command + if: steps.parse.outputs.command != '' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ steps.parse.outputs.issue_number }}, + body: `🤖 Command received and delegated to Claude agent.\n\n---\nProcessed by Control Center Delegator` + }); diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml new file mode 100644 index 00000000..4c54a487 --- /dev/null +++ b/.github/workflows/review.yml @@ -0,0 +1,95 @@ +name: Review +# Consolidated PR review automation workflow +# Replaces: ai-reviewer, ecosystem-reviewer, ecosystem-reviewer-local + +on: + pull_request: + types: [opened, synchronize, reopened] + workflow_call: + inputs: + pr_number: + description: 'PR number to review' + required: true + type: string + repository: + description: 'Repository (owner/repo)' + required: true + type: string + workflow_dispatch: + inputs: + pr_number: + description: 'PR number to review' + required: true + type: string + repository: + description: 'Repository (owner/repo)' + required: false + type: string + +env: + OLLAMA_HOST: ${{ vars.OLLAMA_HOST || 'https://ollama.com' }} + OLLAMA_MODEL: ${{ vars.OLLAMA_MODEL || 'glm-4.6:cloud' }} + +permissions: + contents: read + pull-requests: write + issues: write + +jobs: + quick-review: + runs-on: ubuntu-latest + steps: + - name: Review with control-center + id: review + run: | + PR_NUM="${{ inputs.pr_number || github.event.pull_request.number }}" + REPO="${{ inputs.repository || github.repository }}" + + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + -e OLLAMA_API_KEY="${OLLAMA_API_KEY}" \ + -e OLLAMA_HOST="${OLLAMA_HOST}" \ + -e OLLAMA_MODEL="${OLLAMA_MODEL}" \ + jbcom/control-center:latest reviewer review \ + --pr "$PR_NUM" \ + --repo "$REPO" \ + --output json > review-result.json + + echo "result<> $GITHUB_OUTPUT + cat review-result.json >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OLLAMA_API_KEY: ${{ secrets.OLLAMA_API_KEY }} + + - name: Post review comment + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const fs = require('fs'); + const review = JSON.parse(fs.readFileSync('review-result.json', 'utf8')); + + let body = `## 🤖 AI Code Review\n\n`; + body += `### Summary\n${review.summary}\n\n`; + + if (review.issues && review.issues.length > 0) { + body += `### Issues Found\n`; + review.issues.forEach(issue => { + const emoji = issue.severity === 'critical' ? '🔴' : + issue.severity === 'high' ? '🟠' : + issue.severity === 'medium' ? '🟡' : '🟢'; + body += `${emoji} **${issue.severity}**: ${issue.description}\n`; + if (issue.suggestion) { + body += ` - 💡 ${issue.suggestion}\n`; + } + }); + } + + body += `\n---\nReviewed by Control Center using ${process.env.OLLAMA_MODEL}`; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request?.number || Number(context.payload.inputs.pr_number), + body: body + }); diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml new file mode 100644 index 00000000..4e5d3906 --- /dev/null +++ b/.github/workflows/triage.yml @@ -0,0 +1,350 @@ +name: Triage +# Consolidated issue & PR triage workflow +# Replaces: ai-curator, ecosystem-curator, ecosystem-curator-local, ecosystem-triage +# +# INTELLIGENT ENTERPRISE HEALTH MANAGEMENT: +# - On push to main: Rebase ALL open PRs, resolve conflicts with autoheal +# - On PR events: Track and triage individual PRs +# - On schedule: Deduplicate issues, check PR health across repository +# - Prevents cascade of conflicts by keeping PRs up-to-date automatically + +on: + push: + branches: + - main + - master + issues: + types: [opened, reopened, labeled] + pull_request: + types: [opened, reopened, labeled, synchronize, ready_for_review] + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' # Every 6 hours for health checks + workflow_dispatch: + inputs: + operation: + description: 'Operation to perform' + required: true + type: choice + options: + - triage_issue + - track_pr + - deduplicate + - rebase_all_prs + - health_check + +permissions: + contents: write + issues: write + pull-requests: write + actions: write + +jobs: + # ============================================ + # ENTERPRISE HEALTH: Keep all PRs up-to-date + # ============================================ + rebase-prs-on-main-push: + runs-on: ubuntu-latest + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master') + steps: + - name: Get all open PRs + id: get-prs + run: | + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator list-prs \ + --repo ${{ github.repository }} \ + --output json > open-prs.json + + echo "count=$(jq length open-prs.json)" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Rebase and update each PR + if: steps.get-prs.outputs.count > 0 + run: | + echo "🔄 Found ${{ steps.get-prs.outputs.count }} open PRs to rebase" + + jq -c '.[]' open-prs.json | while read -r pr; do + PR_NUM=$(echo "$pr" | jq -r '.number') + PR_BRANCH=$(echo "$pr" | jq -r '.head.ref') + PR_TITLE=$(echo "$pr" | jq -r '.title') + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "🔄 Rebasing PR #$PR_NUM: $PR_TITLE" + echo " Branch: $PR_BRANCH" + + # Check if PR has conflicts + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator check-conflicts \ + --pr "$PR_NUM" \ + --repo ${{ github.repository }} \ + --output json > pr-$PR_NUM-status.json + + HAS_CONFLICTS=$(jq -r '.has_conflicts' pr-$PR_NUM-status.json) + IS_BEHIND=$(jq -r '.behind_base' pr-$PR_NUM-status.json) + + if [ "$IS_BEHIND" = "true" ] || [ "$HAS_CONFLICTS" = "true" ]; then + echo "⚠️ PR is behind base or has conflicts" + + # Attempt automatic rebase + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator rebase-pr \ + --pr "$PR_NUM" \ + --repo ${{ github.repository }} \ + --auto-resolve \ + --output json > pr-$PR_NUM-rebase.json + + REBASE_STATUS=$(jq -r '.status' pr-$PR_NUM-rebase.json) + + if [ "$REBASE_STATUS" = "success" ]; then + echo "✅ Successfully rebased and pushed" + + # Comment on PR + MESSAGE="🤖 **Automatic Rebase Complete** + + This PR was automatically rebased against the latest \`${{ github.ref_name }}\` after recent changes were merged. + + ✅ No conflicts detected + 🔄 Branch is now up-to-date + + --- + Performed by Control Center Triage" + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator comment \ + --pr "$PR_NUM" \ + --repo ${{ github.repository }} \ + --message "$MESSAGE" + + elif [ "$REBASE_STATUS" = "conflicts" ]; then + echo "⚠️ Conflicts detected, triggering autoheal" + + # Trigger autoheal workflow to resolve conflicts + gh workflow run autoheal.yml \ + -f operation=resolve_conflicts \ + -f pr_number="$PR_NUM" \ + -f repository="${{ github.repository }}" + + # Comment on PR + MESSAGE="🤖 **Automatic Rebase - Conflicts Detected** + + This PR has conflicts with the latest \`${{ github.ref_name }}\` branch. + + ⚠️ Conflicts found during automatic rebase + 🔧 Triggered autoheal workflow to resolve conflicts + ⏳ AI-powered conflict resolution in progress + + The autoheal workflow will analyze the conflicts and attempt to resolve them automatically. Check back soon for updates. + + --- + Performed by Control Center Triage" + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator comment \ + --pr "$PR_NUM" \ + --repo ${{ github.repository }} \ + --message "$MESSAGE" + + else + echo "❌ Rebase failed: $REBASE_STATUS" + + # Comment on PR with failure details + FAILURE_REASON=$(jq -r '.error' pr-$PR_NUM-rebase.json) + MESSAGE="🤖 **Automatic Rebase Failed** + + This PR could not be automatically rebased against \`${{ github.ref_name }}\`. + + ❌ Error: $FAILURE_REASON + + Please manually rebase your branch or resolve conflicts locally: + \`\`\`bash + git fetch origin + git rebase origin/${{ github.ref_name }} + git push --force-with-lease + \`\`\` + + --- + Performed by Control Center Triage" + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator comment \ + --pr "$PR_NUM" \ + --repo ${{ github.repository }} \ + --message "$MESSAGE" + fi + else + echo "✅ PR is up-to-date, no action needed" + fi + + echo "" + done + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate health report + if: always() + run: | + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator health-report \ + --repo ${{ github.repository }} \ + --output markdown > health-report.md + + cat health-report.md >> $GITHUB_STEP_SUMMARY + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # ============================================ + # INDIVIDUAL PR TRIAGE + # ============================================ + triage-pr: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - name: Triage PR + run: | + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator triage-pr \ + --pr ${{ github.event.pull_request.number }} \ + --repo ${{ github.repository }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if PR is behind base + if: github.event.action == 'opened' || github.event.action == 'reopened' + run: | + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator check-conflicts \ + --pr ${{ github.event.pull_request.number }} \ + --repo ${{ github.repository }} \ + --output json > pr-status.json + + # Check if PR is behind and notify + IS_BEHIND=$(jq -r '.behind_base // false' pr-status.json) + if [ "$IS_BEHIND" = "true" ]; then + MESSAGE="ℹ️ This PR is behind the base branch. Consider rebasing to include the latest changes." + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator comment \ + --pr ${{ github.event.pull_request.number }} \ + --repo ${{ github.repository }} \ + --message "$MESSAGE" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # ============================================ + # ISSUE TRIAGE + # ============================================ + triage-issue: + runs-on: ubuntu-latest + if: github.event_name == 'issues' && github.event.action == 'opened' + steps: + - name: Triage new issue + run: | + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator triage \ + --issue ${{ github.event.issue.number }} \ + --repo ${{ github.repository }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # ============================================ + # SCHEDULED HEALTH CHECKS + # ============================================ + scheduled-health-check: + runs-on: ubuntu-latest + if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.operation == 'health_check') + steps: + - name: Deduplicate issues + run: | + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator deduplicate \ + --repo ${{ github.repository }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check PR health + run: | + echo "🏥 Performing repository health check..." + + # Check all open PRs for staleness, conflicts, outdated branches + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator health-check \ + --repo ${{ github.repository }} \ + --check-prs \ + --check-stale \ + --check-conflicts \ + --auto-notify \ + --output json > health-check.json + + # Generate report + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + -v "${{ github.workspace }}:/workspace" \ + -w /workspace \ + jbcom/control-center:latest curator health-report \ + --input health-check.json \ + --output markdown > health-report.md + + cat health-report.md >> $GITHUB_STEP_SUMMARY + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Auto-rebase stale PRs + run: | + echo "🔄 Auto-rebasing PRs that are significantly behind..." + + # Get PRs that are >10 commits behind + jq -r '.prs[] | select(.behind_by > 10) | .number' health-check.json | while read -r PR_NUM; do + echo "Triggering rebase for PR #$PR_NUM" + + gh workflow run triage.yml \ + -f operation=rebase_all_prs + done + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # ============================================ + # MANUAL OPERATIONS + # ============================================ + manual-operation: + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' && inputs.operation != 'health_check' + steps: + - name: Execute operation + run: | + case "${{ inputs.operation }}" in + triage_issue) + echo "Manual issue triage not yet implemented" + ;; + track_pr) + echo "Manual PR tracking not yet implemented" + ;; + deduplicate) + docker run --rm \ + -e GITHUB_TOKEN="${GITHUB_TOKEN}" \ + jbcom/control-center:latest curator deduplicate --repo ${{ github.repository }} + ;; + rebase_all_prs) + echo "🔄 Manually triggered: Rebase all open PRs" + gh workflow run triage.yml # Trigger push event handler + ;; + *) + echo "Unknown operation: ${{ inputs.operation }}" + exit 1 + ;; + esac + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}