diff --git a/apps/docs/providers/source-control/github.mdx b/apps/docs/providers/source-control/github.mdx index 9fc69c78e..03b0eeeea 100644 --- a/apps/docs/providers/source-control/github.mdx +++ b/apps/docs/providers/source-control/github.mdx @@ -80,6 +80,7 @@ Grant these repository permissions: - **Code scanning alerts**: Read-only - **Issues**: Read and write - **Metadata**: Read-only +- **Organization projects**: Read and write - **Pull requests**: Read and write - **Workflows**: Read and write @@ -155,6 +156,34 @@ Install the GitHub App on the repositories Roomote should use. Then sign in, complete the GitHub step in `/setup`, and confirm repository sync sees the expected repositories. +If you add Organization projects permission to an existing app, an organization +owner must approve the updated permission before Roomote can manage project +items. + +## Daily GitHub management + +Roomote tasks can manage routine repository work through the GitHub App +installation. Requests default to the repository mapped to the task: + +- list, create, edit, delete, and apply labels to issues or pull requests +- create, update, delete, and report progress for milestones; assign issues to milestones +- list accessible GitHub Projects V2, inspect an issue or pull request's Status, + and update an existing project item's single-select Status field + +Read-only requests, creations, and additive label actions run directly. Before +editing, replacing, assigning, clearing, moving, or deleting an item, Roomote +shows the proposed change and asks for confirmation. Deletions name the resource +and URL in that confirmation. After a write, Roomote reads the result back and +shares its GitHub URL. + +Projects V2 operations require the **Organization projects** permission above. +If a project or Status field is ambiguous, Roomote asks you to choose one. An +issue or pull request that is not already on a project is reported as such; +Roomote does not add it automatically. + +Native GitHub saved views are not currently supported. Roomote can share an +issue-search URL, but it does not represent that URL as a saved GitHub view. + ## Start work from issues and pull requests Once the app is installed and an environment maps the repository: diff --git a/apps/web/src/trpc/commands/github/mutations.test.ts b/apps/web/src/trpc/commands/github/mutations.test.ts index fc066fc7d..a71f291d1 100644 --- a/apps/web/src/trpc/commands/github/mutations.test.ts +++ b/apps/web/src/trpc/commands/github/mutations.test.ts @@ -165,6 +165,7 @@ describe('GitHub App manifest commands', () => { issues: 'write', merge_queues: 'read', metadata: 'read', + organization_projects: 'write', pull_requests: 'write', security_events: 'read', statuses: 'read', diff --git a/apps/web/src/trpc/commands/github/mutations.ts b/apps/web/src/trpc/commands/github/mutations.ts index 8135a4768..0dc1fcd91 100644 --- a/apps/web/src/trpc/commands/github/mutations.ts +++ b/apps/web/src/trpc/commands/github/mutations.ts @@ -105,6 +105,7 @@ type GitHubAppManifest = { issues: 'write'; merge_queues: 'read'; metadata: 'read'; + organization_projects: 'write'; pull_requests: 'write'; security_events: 'read'; statuses: 'read'; @@ -231,6 +232,7 @@ function buildGitHubAppManifest(): GitHubAppManifest { issues: 'write', merge_queues: 'read', metadata: 'read', + organization_projects: 'write', pull_requests: 'write', security_events: 'read', statuses: 'read', diff --git a/apps/worker/src/mcp/roomote-mcp-server/integration-setup.ts b/apps/worker/src/mcp/roomote-mcp-server/integration-setup.ts index 17b617265..a9c25435a 100644 --- a/apps/worker/src/mcp/roomote-mcp-server/integration-setup.ts +++ b/apps/worker/src/mcp/roomote-mcp-server/integration-setup.ts @@ -21,7 +21,7 @@ An admin connects Linear from Settings > Integrations. This uses OAuth to link t # GitHub -GitHub is connected via a GitHub App installation. An admin installs the Roomote GitHub App on the relevant GitHub organization/repos from Settings > Integrations. Once installed, I can access repo context through the GitHub App installation. Automatic PR review and direct PR @roomote follow-up or re-review requests also require Code Reviewer to be enabled. Automatic review still follows the current reviewer gate, while direct PR mentions are manual requests in the PR thread rather than part of that proactive gate. No per-user OAuth is needed since access goes through the GitHub App installation. +GitHub is connected via a GitHub App installation. An admin installs the Roomote GitHub App on the relevant GitHub organization/repos from Settings > Integrations. Once installed, I can access repo context through the GitHub App installation. Tasks can manage repository labels and milestones, and can inspect or update accessible GitHub Projects V2 Status fields after confirming mutations. Projects require the GitHub App's Organization projects permission and installation-owner approval. Native GitHub saved views are not available until their API and installation-token compatibility are verified. Automatic PR review and direct PR @roomote follow-up or re-review requests also require Code Reviewer to be enabled. Automatic review still follows the current reviewer gate, while direct PR mentions are manual requests in the PR thread rather than part of that proactive gate. No per-user OAuth is needed since access goes through the GitHub App installation. # Notion diff --git a/packages/cloud-agents/src/packaged-skill-invocations.ts b/packages/cloud-agents/src/packaged-skill-invocations.ts index 858d5721c..1f3501eee 100644 --- a/packages/cloud-agents/src/packaged-skill-invocations.ts +++ b/packages/cloud-agents/src/packaged-skill-invocations.ts @@ -21,6 +21,7 @@ const CORE_PACKAGED_SKILL_INVOCATIONS = [ 'environment-setup', 'explain-repo-code', 'fix-pr', + 'github-management', 'implement-repo-change', 'implement-changes', 'merge-resolution-review', diff --git a/packages/cloud-agents/src/server/workflows/__tests__/githubManagementSkill.test.ts b/packages/cloud-agents/src/server/workflows/__tests__/githubManagementSkill.test.ts new file mode 100644 index 000000000..c00dbe278 --- /dev/null +++ b/packages/cloud-agents/src/server/workflows/__tests__/githubManagementSkill.test.ts @@ -0,0 +1,35 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const thisDirPath = path.dirname(fileURLToPath(import.meta.url)); + +function readGitHubManagementSkill(): string { + return fs.readFileSync( + path.resolve(thisDirPath, '../skills/standard/github-management/SKILL.md'), + 'utf8', + ); +} + +describe('github-management skill', () => { + it('requires confirmation for destructive GitHub mutations and reads back every write', () => { + const skill = readGitHubManagementSkill(); + + expect(skill).toContain('Creations and additive actions run directly'); + expect(skill).toContain('Before a destructive change'); + expect(skill).toContain('use `request_user_input`'); + expect(skill).toContain('After a write, read back'); + expect(skill).toContain( + 'Before deletion, show the exact resource name and URL', + ); + }); + + it('uses the scoped GitHub CLI contract and gates native saved views', () => { + const skill = readGitHubManagementSkill(); + + expect(skill).toContain('use `gh api` only'); + expect(skill).toContain('`gh api graphql`'); + expect(skill).toContain('Native GitHub saved views are not supported'); + expect(skill).toContain('do not inject a linked user'); + }); +}); diff --git a/packages/cloud-agents/src/server/workflows/__tests__/standardTaskExplicitInvocationRouting.test.ts b/packages/cloud-agents/src/server/workflows/__tests__/standardTaskExplicitInvocationRouting.test.ts index 89ae86183..d5d1fce03 100644 --- a/packages/cloud-agents/src/server/workflows/__tests__/standardTaskExplicitInvocationRouting.test.ts +++ b/packages/cloud-agents/src/server/workflows/__tests__/standardTaskExplicitInvocationRouting.test.ts @@ -113,6 +113,16 @@ describe('Standard Task explicit invocation routing', () => { expect(prompt.startsWith('$agent-browser\n')).toBe(true); }); + it('treats GitHub management invocations as packaged-skill entry', () => { + const { prompt } = standardTask({ + description: '$github-management\n\nCreate a priority label.', + repo: 'Roomote/example-app', + requestFormat: 'structured', + }); + + expect(prompt.startsWith('$github-management\n')).toBe(true); + }); + it('treats productized Sentry triage invocations as packaged-skill entry in customer repos', () => { const { prompt } = standardTask({ description: diff --git a/packages/cloud-agents/src/server/workflows/skills/standard/github-management/SKILL.md b/packages/cloud-agents/src/server/workflows/skills/standard/github-management/SKILL.md new file mode 100644 index 000000000..a3d6c4a8e --- /dev/null +++ b/packages/cloud-agents/src/server/workflows/skills/standard/github-management/SKILL.md @@ -0,0 +1,59 @@ +--- +name: github-management +description: Manage repository labels, milestones, and GitHub Projects V2 safely through the scoped GitHub App credential. Use for requests to list, create, edit, delete, assign, or move GitHub management resources. +--- + +# GitHub Management + + +You manage routine GitHub repository operations with the task's scoped GitHub App credential. Read state with the available GitHub tools when useful, and use `gh api` only for the defined GitHub mutations below. Do not add a writable provider-neutral MCP surface or use a linked user's OAuth token. + + + +- Default to the repository mapped to the task. An explicitly named repository must be verified as available to the current GitHub App installation before use. +- Support labels, milestones, and organization GitHub Projects V2 that the installation can access. +- Native GitHub saved views are not supported until the capability check below confirms an installation-token-compatible API. Do not describe a generated issue-search URL as a saved view. +- Do not add an issue to a project, change non-single-select fields, or operate on a user-owned project without a separate capability and product decision. + + + +- Read-only requests run without confirmation. +- Creations and additive actions run directly. This includes creating labels or milestones and adding labels to an issue or pull request. +- Before a destructive change, read the current state, show the exact proposed change, and use `request_user_input` to obtain an explicit confirmation. Destructive changes are editing or deleting a label or milestone, replacing labels, assigning or clearing a milestone, and updating a project Status value. +- Before deletion, show the exact resource name and URL as part of that confirmation. Do not infer confirmation from a broad request or a prior confirmation for another operation. +- If the requested payload changes after preflight, discard the prior confirmation and repeat the preflight. +- After a write, read back the affected resource and report its URL and resulting state. The task conversation is the audit record; do not create a database audit record. +- Encode user-controlled path segments and pass user values through `gh api` fields or quoted arguments. Never interpolate them into executable shell fragments. + + + +- List and inspect labels through `/repos/{owner}/{repo}/labels`. +- Create labels with `POST /repos/{owner}/{repo}/labels`; edit with `PATCH /repos/{owner}/{repo}/labels/{name}`; delete with `DELETE /repos/{owner}/{repo}/labels/{name}`. +- Apply labels to an issue or pull request through `/repos/{owner}/{repo}/issues/{number}/labels`. Applying labels is additive unless the user explicitly asks to replace labels; replacement preflight must show both removed and added labels. +- Validate the issue or pull request number, label existence, duplicate names, and GitHub label-color format before confirmation. + + + +- List and inspect milestones through `/repos/{owner}/{repo}/milestones`. +- Create with `POST /repos/{owner}/{repo}/milestones`, edit with `PATCH /repos/{owner}/{repo}/milestones/{number}`, and delete with `DELETE /repos/{owner}/{repo}/milestones/{number}`. +- Assign or clear a milestone through `PATCH /repos/{owner}/{repo}/issues/{number}` with the resolved milestone number or `null`. +- Report progress from the milestone's open and closed issue counts. Validate dates, milestone state, and issue existence before confirmation. + + + +- Use `gh api graphql` to discover organization projects, project fields, project items, and single-select option IDs. Do not assume a board schema or column position. +- Resolve the requested project and field by name. The default field is `Status` only when it is a single-select field with that exact name. When a project or field is ambiguous, list the choices and ask the user to select one. +- Locate the issue or pull request's existing item. If it is not in the project, report that fact instead of adding it. +- Before calling `updateProjectV2ItemFieldValue`, show the project, field, current value, requested option, and item, then obtain confirmation. After confirmation, read back and report the project URL and final value. +- When GitHub returns a missing permission error, explain that the GitHub App needs Organization projects access and the installation owner must approve the updated permission. Do not report the project as missing. + + + +Before adding any native saved-view command, verify the exact GitHub endpoint, resource ownership, response URL, and whether an installation token can list, create, and delete the resource. Also verify whether the existing linked-account OAuth scope is sufficient. Task sandboxes intentionally use only scoped installation tokens, so do not inject a linked user's broader OAuth token into the sandbox. If the check fails, keep native views unavailable and offer a clearly labeled issue-search URL only as a non-persistent fallback. + + + +- Every destructive mutation had the appropriate preflight and explicit confirmation; every write had a read-back. +- The response includes the resulting GitHub URL or explains the concrete permission/capability blocker. +- No operation used a broader credential or an unsupported saved-view API. +