From e3e7731547abb4bb4e0149ed50c8bf426129a8db Mon Sep 17 00:00:00 2001 From: MervinPraison <454862+MervinPraison@users.noreply.github.com> Date: Wed, 22 Apr 2026 06:08:37 +0100 Subject: [PATCH] chore(ci): expand claude.yml + add auto-issue-comment.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - claude.yml: add issues:[opened,labeled,assigned] + pull_request_review_comment triggers - claude.yml: add issue-triage job (welcome comment on new issues) - claude.yml: expand direct_prompt with PraisonAIUI-specific routing (STEP 0-6): - architecture routing (core / features / UI builders / frontend / tests / docs) - external-repo routing (PraisonAI / PraisonAI-Tools / PraisonAIDocs) - conceptual checks (no duplicate exports, no class-first Ask*Message, lazy optional deps, real DeprecationWarning on deprecations) - import-time < 200ms + no heavy-dep leak invariant - PR body template (Summary, Before/After, SHA-cited checklist, test evidence, import-time proof) - STEP 6 reviewer-loop instructions (respond to CodeRabbit/Copilot/human review comments, push to same branch) - auto-issue-comment.yml: optional workflow to escalate an issue to Claude by adding the 'claude' label (workflow_dispatch-only; issues:opened trigger commented out by default) CodeRabbit + Copilot: no workflow file needed — both are GitHub Apps that react to PRs automatically. The chain (CodeRabbit → Copilot → Claude) is already orchestrated by auto-pr-comment.yml. --- .github/workflows/auto-issue-comment.yml | 86 ++++++++ .github/workflows/claude.yml | 252 ++++++++++++++++++++--- 2 files changed, 311 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/auto-issue-comment.yml diff --git a/.github/workflows/auto-issue-comment.yml b/.github/workflows/auto-issue-comment.yml new file mode 100644 index 0000000..d89666e --- /dev/null +++ b/.github/workflows/auto-issue-comment.yml @@ -0,0 +1,86 @@ +name: Auto Issue Comment + +# Optional: automatically escalate every new issue to Claude for a full +# investigation + draft fix. Disabled by default (only workflow_dispatch). +# To enable auto-triage on every new issue, uncomment the `issues:` trigger. +# +# Note: `@claude` mentions only trigger claude.yml when the commenter is a +# HUMAN user (or github-actions[bot] via PAT). This workflow posts the +# comment as github-actions[bot], so claude.yml must allow that bot. + +on: + workflow_dispatch: + inputs: + issue_number: + description: "Issue number to triage with Claude" + required: true + type: number + # issues: + # types: [opened] + +jobs: + # Escalate an issue to full Claude implementation by adding the `claude` + # label. claude.yml listens on `issues:labeled` + label=='claude' and + # triggers Job 2 (claude-response) which does the actual work. + trigger-claude: + if: | + github.event_name == 'workflow_dispatch' || + (github.event_name == 'issues' && !contains(toJSON(github.event.issue.labels), 'claude')) + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Resolve issue number + id: resolve + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "number=${{ inputs.issue_number }}" >> "$GITHUB_OUTPUT" + else + echo "number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT" + fi + + - name: Add claude label + post instructions + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const issueNumber = Number('${{ steps.resolve.outputs.number }}'); + const { data: issue } = await github.rest.issues.get({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + }); + + const labels = (issue.labels || []).map(l => typeof l === 'string' ? l : l.name); + if (labels.includes('claude')) { + console.log(`Issue #${issueNumber} already has 'claude' label — skipping.`); + return; + } + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + labels: ['claude'], + }); + + const body = [ + '🤖 Auto-triaging with Claude.', + '', + 'Claude will:', + '', + '1. Read this issue and all comments.', + '2. Classify and route to the right area (`src/praisonaiui/`, `src/frontend/`, `docs/`, or external repo).', + '3. Implement a minimal fix per `AGENTS.md`.', + '4. Run `pytest` + the import-time invariant locally.', + '5. Open a draft PR linked to this issue.', + '', + 'Follow-ups in this thread with `@claude` will re-trigger the loop.' + ].join('\n'); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body, + }); diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index a39becd..f74e2fc 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -1,8 +1,26 @@ name: Claude Assistant +# Triggers: +# - issues:opened → lightweight triage (welcome comment + label) +# - issues:labeled → full implementation when `claude` label applied +# - issues:assigned → full implementation when issue assigned to Claude +# - issue_comment → full implementation when @claude mentioned (issues + PRs) +# - pull_request_review → full implementation when @claude mentioned in review +# - pull_request_review_comment → full implementation when @claude mentioned inline +# +# Focus: CLAUDE only. Copilot and CodeRabbit are GitHub Apps that react to PRs +# automatically — no workflow file needed. auto-pr-comment.yml orchestrates the +# CodeRabbit → Copilot → Claude review chain for PRs. + on: issue_comment: types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, labeled, assigned] + pull_request_review: + types: [submitted] permissions: contents: write @@ -12,15 +30,74 @@ permissions: id-token: write jobs: + # ================================================================ + # Job 1 — Issue triage (runs on ANY new issue, including from external users) + # Posts a welcome comment. Does NOT execute Claude Code. + # To escalate to full implementation, a maintainer adds the `claude` label + # or mentions @claude in a comment — which triggers Job 2. + # ================================================================ + issue-triage: + if: | + github.event_name == 'issues' && + github.event.action == 'opened' && + !contains(toJSON(github.event.issue.labels), 'claude') + runs-on: ubuntu-latest + permissions: + issues: write + contents: read + steps: + - name: Post welcome comment + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const body = [ + '👋 Thanks for opening an issue in **PraisonAIUI**.', + '', + 'A maintainer will triage this shortly. If you would like Claude to investigate and draft a fix, a maintainer can:', + '', + '- Apply the `claude` label to this issue, **or**', + '- Mention `@claude` in a comment with any additional instructions.', + '', + 'Please make sure your issue includes:', + '', + '1. A clear description of the behaviour you expect vs what you see.', + '2. Minimal reproduction steps (code snippet or `aiui.template.yaml`).', + '3. Version: `python -c "import praisonaiui; print(praisonaiui.__version__)"`.', + '4. Any relevant log output.', + '', + 'See [AGENTS.md](https://github.com/MervinPraison/PraisonAIUI/blob/main/AGENTS.md) for project conventions.' + ].join('\n'); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body + }); + + # ================================================================ + # Job 2 — Full Claude Code implementation + # Runs when: + # - A comment containing `@claude` is posted on an issue or PR + # - The `claude` label is added to an issue + # - The issue is assigned to a Claude bot + # - A PR review or review-comment contains `@claude` + # ================================================================ claude-response: env: - ISSUE_NUMBER_RESOLVED: ${{ github.event.issue.number }} + ISSUE_NUMBER_RESOLVED: ${{ github.event.issue.number || github.event.pull_request.number }} if: | - github.event_name == 'issue_comment' && - contains(github.event.comment.body, '@claude') && github.actor != 'dependabot[bot]' && github.actor != 'cursor[bot]' && - github.actor != 'renovate[bot]' + github.actor != 'renovate[bot]' && + ( + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && github.event.action == 'labeled' && contains(toJSON(github.event.label), 'claude')) || + (github.event_name == 'issues' && github.event.action == 'assigned') + ) runs-on: ubuntu-latest steps: - name: Checkout repository @@ -42,46 +119,167 @@ jobs: allowed_bots: "praisonai-triage-agent[bot]" timeout_minutes: "30" direct_prompt: | - You are implementing changes in MervinPraison/PraisonAIUI (Server-Driven UI). Follow AGENTS.md in this repository. + You are implementing changes in **MervinPraison/PraisonAIUI** (YAML-driven server-driven UI with agentic chat). + Follow `AGENTS.md` at the repository root. If a conflict arises between this prompt and `AGENTS.md`, `AGENTS.md` wins. - Primary scope: src/praisonaiui/, tests/, docs/. If the fix belongs in praisonaiagents, praisonai, or PraisonAI-Tools, route there and explain — do not implement SDK/tool code inside this repo unless it is AIUI integration only. - - STEP 0 — GIT & GITHUB CLI: + ═══════════════════════════════════════════════════════════════ + STEP 0 — GIT & GITHUB CLI + ═══════════════════════════════════════════════════════════════ git config --global user.name "github-actions[bot]" git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" gh auth setup-git - STEP 1 — ISSUE: - Read the issue and all comments. Implement what the issue describes with minimal, focused changes. + ═══════════════════════════════════════════════════════════════ + STEP 1 — READ GUIDELINES + ═══════════════════════════════════════════════════════════════ + Read `AGENTS.md` and any `docs/principles.md`. Key invariants: + • Cold import of `praisonaiui` must stay under 200 ms; module count must not grow. + • Heavy / optional deps (`mcp`, `langchain`, `llama_index`, `semantic_kernel`, + `slack_sdk`, `discord.py`, `botbuilder.core`, `openai`, `anthropic`, `mistralai`, + `google.generativeai`) must be **lazy-imported** — never eager. + • Public API is verb-first. New classes in `src/praisonaiui/message.py` ending + in `Message` with a verb prefix (Ask*, Do*, Request*, …) are forbidden — + use a module-level `async def` verb helper instead (see `prompt`, `error`). + • Deprecations must emit a runtime `warnings.warn(..., DeprecationWarning, stacklevel=2)` + on first access, not only in the docstring. + • Deterministic serialisation in anything reaching the wire (sorted keys, stable field + order). See existing `to_dict` patterns. + • Multi-agent + async safe: no global mutable state without a lock. - STEP 2 — BRANCH (single name; reuse in STEP 4): - BRANCH="claude/issue-${{ env.ISSUE_NUMBER_RESOLVED }}-$(date +%Y%m%d-%H%M%S)" - git checkout -b "$BRANCH" + ═══════════════════════════════════════════════════════════════ + STEP 2 — ARCHITECTURE VALIDATION & ROUTING (MANDATORY before any code) + ═══════════════════════════════════════════════════════════════ + Answer these questions inline in your response, then proceed only if you pass: - STEP 3 — TEST / VERIFY: - Run targeted pytest or smoke checks relevant to your edits (e.g. pytest tests/unit -q). + ROUTING: + 1. **Core aiui** (`src/praisonaiui/`) — protocols, message types, server, features. + 2. **Features** (`src/praisonaiui/features/`) — opt-in capabilities (MCP, platforms, lifecycle, audio, auth, sharing, etc.). + 3. **UI builders** (`src/praisonaiui/ui.py`) — declarative component helpers. + 4. **Frontend** (`src/frontend/`) — React components / stores / SSE handlers. + 5. **Tests** (`tests/unit/`, `tests/integration/`) — new features require both smoke and real-agentic tests. + 6. **Docs** (`docs/`) — canonical name per feature, migration table for renames. - STEP 4 — COMMIT, PUSH, OPEN PR (mandatory if you produced commits): - git add -A - git commit -m "feat: (fixes #${{ env.ISSUE_NUMBER_RESOLVED }})" - git push -u origin "$BRANCH" - REPO="${{ github.repository }}" - COUNT=$(gh pr list --repo "$REPO" --head "$BRANCH" --json number --jq 'length') - if [ "$COUNT" -gt 0 ]; then - gh pr list --repo "$REPO" --head "$BRANCH" --json url --jq '.[0].url' - else - gh pr create --repo "$REPO" --base main --head "$BRANCH" --title "feat: " --body "Fixes #${{ env.ISSUE_NUMBER_RESOLVED }}" - fi - CRITICAL: Do not stop after `git push`. You MUST complete `gh pr create` (or print the existing PR URL from the step above) and post that URL on the issue. Never ask a human to open the PR manually. + EXTERNAL REPOS (do NOT edit from within this repo — route the work to the right place and stop): + • Core SDK: `MervinPraison/PraisonAI` (`praisonaiagents`, `praisonai`) — agent runtime. + • Tools: `MervinPraison/PraisonAI-Tools` — tool implementations. + • Docs site: `MervinPraison/PraisonAIDocs` — product docs. + If the requested change belongs in an external repo, comment on the issue explaining + where it should live and DO NOT open a PR in this repo. + + CONCEPTUAL CHECKS (if ANY fails — add a comment, do NOT open a PR): + • Does it duplicate an existing public symbol in `src/praisonaiui/__init__.py`? + • Does it add new class-first message types (`class Ask*Message`, `class *Message` + with a verb in the name)? → Use a verb-first helper instead. + • Does it add a new hard dependency to `pyproject.toml` `[project] dependencies`? + → Move to `[project.optional-dependencies]` and wrap imports lazily. + • Does it break backward compat (rename, signature change, removal)? → Add + a deprecation alias that emits `DeprecationWarning`; never hard-remove before the + documented removal version (e.g. `0.5.0`). + + ═══════════════════════════════════════════════════════════════ + STEP 3 — IMPLEMENT + ═══════════════════════════════════════════════════════════════ + Single branch for the whole flow (reuse the same name in STEP 5): + BRANCH="claude/issue-${{ env.ISSUE_NUMBER_RESOLVED }}-$(date +%Y%m%d-%H%M%S)" + git checkout -b "$BRANCH" + + Make minimal, focused changes: + • Stay within the files listed in the issue's "Scope" / "Files to change" section. + • Prefer extending existing modules over adding new ones. + • No emojis, no comments, no markdown except where the user asked for it. + • Python ≥ 3.10 style (`list[...]` not `List[...]`, `X | None` not `Optional[X]`). + + ═══════════════════════════════════════════════════════════════ + STEP 4 — TEST / VERIFY + ═══════════════════════════════════════════════════════════════ + Run these targeted checks (NOT the full suite): + + # 1. Lint on only the files you changed: + CHANGED=$(git diff --name-only origin/main...HEAD | grep -E '\.py$' | tr '\n' ' ') + [ -n "$CHANGED" ] && ruff check $CHANGED + + # 2. Unit tests for the module(s) you touched: + pytest tests/unit/.py -v --tb=short + + # 3. Import-time invariant (MUST stay under 200 ms cold): + python -c "import time, sys; t=time.time(); import praisonaiui; dt=(time.time()-t)*1000; heavy=[m for m in sys.modules if any(h in m for h in ['langchain','llama_index','mcp','slack','discord','botbuilder','openai.','anthropic.','mistralai','google.generativeai'])]; print(f'{dt:.1f}ms modules={len(sys.modules)} leaked={heavy}'); assert heavy == [], f'Heavy deps leaked: {heavy}'" + + # 4. Real agentic end-to-end test (write this into tests/integration/ + # if your feature changes the agent-facing API): + # from praisonaiagents import Agent + # import praisonaiui as aiui + # agent = Agent(name="demo", instructions="You are a helpful assistant") + # result = agent.start("Say hello in one sentence") + # print(result) + + If anything fails, fix the root cause (never skip, xfail, or delete the test). + + ═══════════════════════════════════════════════════════════════ + STEP 5 — COMMIT, PUSH, OPEN PR (MANDATORY if you produced commits) + ═══════════════════════════════════════════════════════════════ + git add -A + git commit -m "feat: (fixes #${{ env.ISSUE_NUMBER_RESOLVED }})" + git push -u origin "$BRANCH" + REPO="${{ github.repository }}" + COUNT=$(gh pr list --repo "$REPO" --head "$BRANCH" --json number --jq 'length') + if [ "$COUNT" -gt 0 ]; then + gh pr list --repo "$REPO" --head "$BRANCH" --json url --jq '.[0].url' + else + gh pr create --repo "$REPO" --base main --head "$BRANCH" \ + --title "feat: " \ + --body "Fixes #${{ env.ISSUE_NUMBER_RESOLVED }} + + ## Summary + + + + ## Before / After + + + + ## Acceptance criteria + + + + ## Test evidence + + \`\`\` + + \`\`\` + + ## Import-time proof + + \`\`\` + + \`\`\` + " + fi + + CRITICAL: Do NOT stop after `git push`. You MUST complete `gh pr create` (or print the + existing PR URL from the step above) and post that URL on the issue. Never ask a + human to open the PR manually. + + ═══════════════════════════════════════════════════════════════ + STEP 6 — RESPOND TO REVIEW FEEDBACK (if this run was triggered by a review comment) + ═══════════════════════════════════════════════════════════════ + If you were invoked by a PR review comment, you are the Lead Engineer for this PR: + • Read ALL reviewer comments in chronological order (CodeRabbit → Copilot → human). + • Fix every VALID issue by pushing commits to THIS branch (do NOT open a new PR). + • Reply with a summary of which comments you addressed, which you disagreed with (and why). + • Never ship a fix that removes/weakens a test. allowed_tools: | Bash(git:*) Bash(python:*) Bash(pip:*) Bash(pytest:*) + Bash(ruff:*) Bash(gh:*) + Bash(npm:*) + Bash(npx:*) Bash(python -m pytest:*) Bash(python -m pip:*) + Bash(python -m ruff:*) View GlobTool GrepTool