The Agent preset creates repositories, with a new repo:create scope (#52)
An agent signed in to MCP with the Agent preset (what Claude Code's plugin gets) could not create a repository: create_repo needed repo:write, which the preset leaves out. repo:write is too much to hand an agent by default: besides creating repositories it changes a repository's merge settings, required checks and approvals (update_repo_settings), updates the repository and its project (update_repo, update_project), creates, renames and deletes branches, publishes and deletes releases, and forks and syncs forks. So repo:create is a new level of Repositories between read and write: it creates repositories and nothing that changes or deletes one. create_repo now needs repo:create, which repo:write and repo:admin include as before, so every token that could create a repository still can. The Agent preset, and with it the OAuth default, adds repo:create. The site's mirror of the table, the token form's level order and label ("Read and create"), the scope checklist, the docs' permission and scope tables, the preset description, the MCP reference, llms.txt and the build-and-ship guide say so; the OpenAPI copy and the CLI's ops table are regenerated. An OAuth grant stores its scopes when it is approved, so a plugin signed in before this keeps what it had until it is signed in again or given repo:create under Connected applications; the guides say so.
- Checked:
- cargo test -p g1t-contracts (313, with a new test that the Agent preset creates repositories and is refused update_repo, update_repo_settings, delete_branch, rename_branch, delete_release, fork_repo, set_repo_visibility, delete_repo and transfer_repo), -p g1t-api (208), -p g1t-identity, -p g1t-actions, -p g1t; cargo check --workspace --tests; apps/web npm test (955 pass) and typecheck against this worktree's contracts; packages/contracts npm test; docs build and npm test.
- G1t-Session:
- ses_01m4ynr1peevvs8zwaqakycv2e
| 774 | 774 | ||
| 775 | 775 | | Permission | Levels | Scope names | | |
| 776 | 776 | | --- | --- | --- | | |
| 777 | − | | Repositories | read, read and write, admin | `repo:read`, `repo:write`, `repo:admin` | | |
| 777 | + | | Repositories | read, read and create, read and write, admin | `repo:read`, `repo:create`, `repo:write`, `repo:admin` | | |
| 778 | 778 | | Code | read, read and write | `code:read`, `code:write` | | |
| 779 | 779 | | Security | read, read and write | `security:read`, `security:write` | | |
| 780 | 780 | | Packages | read, read and write, read, write and delete | `packages:read`, `packages:write`, `packages:delete` | | |
| ⋯ | |||
| 839 | 839 | | Preset | Permissions | | |
| 840 | 840 | | --- | --- | | |
| 841 | 841 | | Read only | Every resource at read except Chat. Changes nothing. | | |
| 842 | − | | Agent | Every resource at read except Self-hosted runners and Chat, and Code, Issues, Pull requests and Notifications at read and write. For a [coding agent](/guides/bring-your-own-agent/): reads everything, works on issues and pull requests, pushes code, and answers your inbox. No admin level. | | |
| 842 | + | | Agent | Every resource at read except Self-hosted runners and Chat, Repositories at read and create, and Code, Issues, Pull requests and Notifications at read and write. For a [coding agent](/guides/bring-your-own-agent/): reads everything, creates repositories, works on issues and pull requests, pushes code, and answers your inbox. It cannot change or delete a repository, its settings, branches or releases. No admin level. | | |
| 843 | 843 | | CI | Repositories: read; Code, Packages, Workflows, Checks and statuses, and Deployments: read and write. Clones and pushes code, pushes and pulls packages, runs workflows, and reports [checks](/guides/checks/) and deployments. | | |
| 844 | 844 | ||
| 845 | 845 | **All** is not a preset that grows: it ticks every permission there is | |
| ⋯ | |||
| 854 | 854 | `issues:write`. A token stores the highest scope of each resource it | |
| 855 | 855 | holds, and every check reads them. A higher level includes the lower ones | |
| 856 | 856 | of the same resource: `repo:admin` includes `repo:write`, which includes | |
| 857 | − | `repo:read`. It never includes another resource: `repo:admin` does not let | |
| 857 | + | `repo:create`, which includes `repo:read`. It never includes another resource: `repo:admin` does not let | |
| 858 | 858 | a token push, which is `code:write`. | |
| 859 | 859 | ||
| 860 | 860 | Applications that [sign in with OAuth](#signing-in-with-oauth) ask for | |
| ⋯ | |||
| 864 | 864 | | Scope | What it lets a token do | | |
| 865 | 865 | | --- | --- | | |
| 866 | 866 | | `repo:read` | See repositories, their settings, labels, timelines, releases, languages, contributors and security alerts, and search | | |
| 867 | + | | `repo:create` | Create repositories, and nothing that changes or deletes one | | |
| 867 | 868 | | `repo:write` | Create repositories, rename branches, change how pull requests merge and publish releases | | |
| 868 | 869 | | `repo:admin` | Rename, archive, transfer, delete or change who can see a repository, and dismiss security alerts | | |
| 869 | 870 | | `code:read` | Clone and fetch private repositories with git | | |
| ⋯ | |||
| 1100 | 1101 | everything you can. | |
| 1101 | 1102 | ||
| 1102 | 1103 | An application that asks for no scopes in particular gets the | |
| 1103 | − | [Agent preset](#presets): every `read` scope except `runners:read` and `chat:read`, and `code:write`, | |
| 1104 | − | `issues:write`, `pull_requests:write` and `notifications:write`. | |
| 1104 | + | [Agent preset](#presets): every `read` scope except `runners:read` and `chat:read`, and `repo:create`, | |
| 1105 | + | `code:write`, `issues:write`, `pull_requests:write` and `notifications:write`. | |
| 1106 | + | The scopes are kept when you approve it: an application approved before | |
| 1107 | + | `repo:create` joined the preset gets it only once you add it under | |
| 1108 | + | Connected applications, or sign it in again. | |
| 1105 | 1109 | It never gets an admin scope unless it asks for one and you leave it | |
| 1106 | 1110 | ticked, and it gets `chat:read` and `chat:write` only by asking for them; | |
| 1107 | 1111 | with them it can use [Chat](#chat-and-notifications-without-the-website). | |
| 41 | 41 | | | The g1t CLI | g1t's MCP tools | | |
| 42 | 42 | | --- | --- | --- | | |
| 43 | 43 | | Signed in by | `g1t auth login` | `/mcp`, **plugin:g1t:g1t** | | |
| 44 | − | | What it may do | Everything you can: the sign-in carries every scope, bounded by your roles | The [Agent preset](/guides/authentication/#presets), unless you change it: reading across g1t, and writing code, issues, pull requests and notifications | | |
| 45 | − | | Creates a repository | Yes | Only once you add `repo:write` | | |
| 44 | + | | What it may do | Everything you can: the sign-in carries every scope, bounded by your roles | The [Agent preset](/guides/authentication/#presets), unless you change it: reading across g1t, creating repositories, and writing code, issues, pull requests and notifications | | |
| 45 | + | | Creates a repository | Yes | Yes, with `repo:create` | | |
| 46 | 46 | ||
| 47 | 47 | The plugin's skills and hooks use the CLI, so Claude reaches for `g1t` | |
| 48 | − | commands first. To let the MCP tools create repositories too, open | |
| 48 | + | commands first. A plugin signed in before the Agent preset gave | |
| 49 | + | `repo:create` keeps the scopes it had: open | |
| 49 | 50 | [Settings → Connected applications](https://g1t.sh/settings/applications), | |
| 50 | − | choose **Change access** on Claude Code, tick `repo:write` and choose | |
| 51 | − | **Save access**. | |
| 51 | + | choose **Change access** on Claude Code, tick `repo:create` and choose | |
| 52 | + | **Save access**, or sign it in again with `/mcp`. | |
| 52 | 53 | ||
| 53 | 54 | ## Build and ship the first version | |
| 54 | 55 | ||
| ⋯ | |||
| 199 | 200 | ||
| 200 | 201 | | | The agent | How | Needs | | |
| 201 | 202 | | --- | --- | --- | --- | | |
| 202 | − | | Create a repository | Yes | `g1t repo create`, or `repository` `create` | `repo:write`, which the Agent preset leaves out | | |
| 203 | + | | Create a repository | Yes | `g1t repo create`, or `repository` `create` | `repo:create`, which the Agent preset gives | | |
| 203 | 204 | | Push code | Yes | git, with the CLI's credentials (`g1t auth setup-git`) or an [access token](/guides/authentication/#access-tokens) | `code:write`; a workflow file also needs `workflow_files:write` | | |
| 204 | 205 | | Open and claim an issue | Yes | `g1t work claim`, or `issue` `claim` | `issues:write` | | |
| 205 | 206 | | Open a pull request and mark it ready | Yes | `g1t pr create` and `g1t pr ready`, or `pull_request` `create` and `ready` | `pull_requests:write` | | |
| 259 | 259 | | [`contents`](/reference/api/repositories/get-contents/) | What it holds at `path` (the root when left out) at `ref` (the default branch when left out): a file's `text` (null for a binary one; at most 200,000 characters), or a directory's `entries` and its README. | `repo` | `code:read` | | |
| 260 | 260 | | [`tarball`](/reference/api/repositories/download-tarball-archive/) | A link to a branch, tag or commit (`ref`, the default branch when left out) as a `.tar.gz` that needs no token, good for 10 minutes, as `url`, with the `commit` it is of. | `repo` | `code:read` | | |
| 261 | 261 | | [`zipball`](/reference/api/repositories/download-zipball-archive/) | The same as a `.zip`. | `repo` | `code:read` | | |
| 262 | − | | [`create`](/reference/api/repositories/create-repo/) | Create a repository in one of your workspaces, empty or as a copy of a public git repository (`import_url`). `description` becomes its [project's](/guides/projects/), as a repository has none of its own. `workspace` may be left out if you belong to exactly one. | `name` | `repo:write` | | |
| 262 | + | | [`create`](/reference/api/repositories/create-repo/) | Create a repository in one of your workspaces, empty or as a copy of a public git repository (`import_url`). `description` becomes its [project's](/guides/projects/), as a repository has none of its own. `workspace` may be left out if you belong to exactly one. | `name` | `repo:create`, which `repo:write` includes and the [Agent preset](/guides/authentication/#presets) gives | | |
| 263 | 263 | | [`update`](/reference/api/repositories/update-repo/) | Change its `website`, `topics` and `default_branch`, whether its default branch is `protected`, whether it is `private`, and whether a private repository may be forked (`allow_forking`). Maintain role; `protected`, `private`, `allow_forking` and `default_branch` need Admin, and `private` the [member privileges](/guides/workspaces/#member-privileges) unless you are an owner. | `repo` | `repo:write` | | |
| 264 | 264 | | [`get_settings`](/reference/api/repositories/get-repo-settings/) | How it handles pull requests: how they merge, and the default branch's required checks, approvals, bypassing checks, being up to date and merge queue as its [rulesets](/guides/rules/) stack. | `repo` | `repo:read` | | |
| 265 | 265 | | [`update_settings`](/reference/api/repositories/update-repo-settings/) | Change those settings. Only the fields given change; `required_checks` replaces the whole list. Maintain role. | `repo` | `repo:write` | |
Binary or large file; its contents are not shown.
| 95 | 95 | return "No access"; | |
| 96 | 96 | case "read": | |
| 97 | 97 | return "Read"; | |
| 98 | + | case "create": | |
| 99 | + | return "Read and create"; | |
| 98 | 100 | case "write": | |
| 99 | 101 | return levelsOf(resource).includes("read") ? "Read and write" : "Write"; | |
| 100 | 102 | case "run": |
| 27 | 27 | type ScopeResource, | |
| 28 | 28 | } from "@g1t/contracts/scopes"; | |
| 29 | 29 | ||
| 30 | − | const RANK: Record<ScopeLevel, number> = { read: 0, write: 1, run: 2, delete: 3, admin: 4 }; | |
| 30 | + | const RANK: Record<ScopeLevel, number> = { read: 0, create: 1, write: 2, run: 3, delete: 4, admin: 5 }; | |
| 31 | 31 | ||
| 32 | 32 | /** Anything with FormData's getters, so tests can pass a plain map. */ | |
| 33 | 33 | export type FormLike = { get(name: string): unknown; getAll(name: string): unknown[] }; |
| 371 | 371 | only public repositories of it, with permissions (`contents`, `issues`, | |
| 372 | 372 | `workflows`, …) mapped to these scopes; a workspace may require an owner | |
| 373 | 373 | to approve one first. For a coding agent, use the Agent preset (every read | |
| 374 | − | scope but `runners:read` and `chat:read`, plus `code:write`, | |
| 374 | + | scope but `runners:read` and `chat:read`, plus `repo:create`, `code:write`, | |
| 375 | 375 | `issues:write`, `pull_requests:write`, `notifications:write`). For CI, the CI preset (`repo:read`, `code:read`, | |
| 376 | 376 | `code:write`, `packages:read`, `packages:write`, `workflows:read`, | |
| 377 | 377 | `workflows:write`, `checks:read`, `checks:write`, `deployments:read`, |
| 9 | 9 | //! | |
| 10 | 10 | //! Each scope is a resource and a level, written `resource:level`, such as | |
| 11 | 11 | //! `issues:write`. A higher level of a resource includes the lower ones: | |
| 12 | − | //! `repo:admin` includes `repo:write`, which includes `repo:read`. | |
| 12 | + | //! `repo:admin` includes `repo:write`, which includes `repo:create`, which | |
| 13 | + | //! includes `repo:read`. | |
| 13 | 14 | //! | |
| 14 | 15 | //! This module is the one source of truth: the API (REST and MCP) and git | |
| 15 | 16 | //! enforce it, and identity stores it. `packages/contracts/src/scopes.ts` | |
| ⋯ | |||
| 170 | 171 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | |
| 171 | 172 | pub enum Level { | |
| 172 | 173 | Read, | |
| 174 | + | /// Making something new without changing what is there: only | |
| 175 | + | /// `repo:create`, which creates repositories and nothing more. | |
| 176 | + | Create, | |
| 173 | 177 | Write, | |
| 174 | 178 | /// Starting g1t's own agents: only `agents:run`, which is retired. | |
| 175 | 179 | Run, | |
| ⋯ | |||
| 182 | 186 | pub fn as_str(self) -> &'static str { | |
| 183 | 187 | match self { | |
| 184 | 188 | Level::Read => "read", | |
| 189 | + | Level::Create => "create", | |
| 185 | 190 | Level::Write => "write", | |
| 186 | 191 | Level::Run => "run", | |
| 187 | 192 | Level::Delete => "delete", | |
| ⋯ | |||
| 195 | 200 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | |
| 196 | 201 | pub enum Scope { | |
| 197 | 202 | RepoRead, | |
| 203 | + | /// Creating repositories, and nothing that changes or deletes one: what | |
| 204 | + | /// the Agent preset gives, so an agent can start a repository. | |
| 205 | + | RepoCreate, | |
| 198 | 206 | RepoWrite, | |
| 199 | 207 | RepoAdmin, | |
| 200 | 208 | CodeRead, | |
| ⋯ | |||
| 253 | 261 | ||
| 254 | 262 | impl Scope { | |
| 255 | 263 | /// Every scope, grouped by resource, least first. | |
| 256 | − | pub const ALL: [Scope; 49] = [ | |
| 264 | + | pub const ALL: [Scope; 50] = [ | |
| 257 | 265 | Scope::RepoRead, | |
| 266 | + | Scope::RepoCreate, | |
| 258 | 267 | Scope::RepoWrite, | |
| 259 | 268 | Scope::RepoAdmin, | |
| 260 | 269 | Scope::CodeRead, | |
| ⋯ | |||
| 308 | 317 | pub fn as_str(self) -> &'static str { | |
| 309 | 318 | match self { | |
| 310 | 319 | Scope::RepoRead => "repo:read", | |
| 320 | + | Scope::RepoCreate => "repo:create", | |
| 311 | 321 | Scope::RepoWrite => "repo:write", | |
| 312 | 322 | Scope::RepoAdmin => "repo:admin", | |
| 313 | 323 | Scope::CodeRead => "code:read", | |
| ⋯ | |||
| 374 | 384 | ||
| 375 | 385 | pub fn level(self) -> Level { | |
| 376 | 386 | match self.as_str().rsplit_once(':').map_or("", |(_, level)| level) { | |
| 387 | + | "create" => Level::Create, | |
| 377 | 388 | "write" => Level::Write, | |
| 378 | 389 | "run" => Level::Run, | |
| 379 | 390 | "delete" => Level::Delete, | |
| ⋯ | |||
| 398 | 409 | pub fn describe(self) -> &'static str { | |
| 399 | 410 | match self { | |
| 400 | 411 | Scope::RepoRead => "See repositories, their settings, labels, timelines, releases, languages, contributors and security alerts, and search", | |
| 412 | + | Scope::RepoCreate => "Create repositories, and nothing that changes or deletes one", | |
| 401 | 413 | Scope::RepoWrite => "Create repositories, create, rename and delete branches, change how pull requests merge and publish releases", | |
| 402 | 414 | Scope::RepoAdmin => "Rename, archive, transfer, delete or change who can see a repository, change its rulesets, and dismiss security alerts", | |
| 403 | 415 | Scope::CodeRead => "Clone and fetch private repositories with git", | |
| ⋯ | |||
| 652 | 664 | let mut scopes: Vec<Scope> = reads().filter(|scope| scope.resource() != Resource::Runners).collect(); | |
| 653 | 665 | // And answering what needs the person it works for: marking | |
| 654 | 666 | // it done, subscribing, watching. | |
| 667 | + | // And starting a repository for what it builds, which changes | |
| 668 | + | // nothing already there: `repo:write` would let it change | |
| 669 | + | // merge settings and delete branches and releases. | |
| 655 | 670 | scopes.extend([ | |
| 671 | + | Scope::RepoCreate, | |
| 656 | 672 | Scope::CodeWrite, | |
| 657 | 673 | Scope::IssuesWrite, | |
| 658 | 674 | Scope::PullRequestsWrite, | |
| ⋯ | |||
| 1047 | 1063 | ("list_deleted_repos", Scope::RepoRead), | |
| 1048 | 1064 | ("list_security_alerts", Scope::RepoRead), | |
| 1049 | 1065 | ("get_codeowners_errors", Scope::RepoRead), | |
| 1050 | − | ("create_repo", Scope::RepoWrite), | |
| 1066 | + | // Creating one changes nothing already there: `repo:create`, which | |
| 1067 | + | // `repo:write` includes. | |
| 1068 | + | ("create_repo", Scope::RepoCreate), | |
| 1051 | 1069 | ("update_repo", Scope::RepoWrite), | |
| 1052 | 1070 | ("update_project", Scope::RepoWrite), | |
| 1053 | 1071 | ("update_repo_settings", Scope::RepoWrite), | |
| ⋯ | |||
| 1543 | 1561 | let (needed, doing) = match level { | |
| 1544 | 1562 | Level::Read => (Scope::PackagesRead, "pull a private package"), | |
| 1545 | 1563 | Level::Delete | Level::Admin => (Scope::PackagesDelete, "delete packages"), | |
| 1546 | − | Level::Write | Level::Run => (Scope::PackagesWrite, "push or publish packages"), | |
| 1564 | + | Level::Create | Level::Write | Level::Run => (Scope::PackagesWrite, "push or publish packages"), | |
| 1547 | 1565 | }; | |
| 1548 | 1566 | if !access.allows(needed) && !(level == Level::Read && public) { | |
| 1549 | 1567 | return Decision::deny( | |
| ⋯ | |||
| 1584 | 1602 | fn a_higher_level_includes_the_lower_ones_of_its_resource_only() { | |
| 1585 | 1603 | assert!(Scope::RepoAdmin.includes(Scope::RepoRead)); | |
| 1586 | 1604 | assert!(Scope::RepoAdmin.includes(Scope::RepoWrite)); | |
| 1605 | + | assert!(Scope::RepoWrite.includes(Scope::RepoCreate)); | |
| 1606 | + | assert!(Scope::RepoCreate.includes(Scope::RepoRead)); | |
| 1607 | + | assert!(!Scope::RepoCreate.includes(Scope::RepoWrite)); | |
| 1587 | 1608 | assert!(Scope::IssuesWrite.includes(Scope::IssuesRead)); | |
| 1588 | 1609 | assert!(!Scope::IssuesRead.includes(Scope::IssuesWrite)); | |
| 1589 | 1610 | assert!(!Scope::RepoAdmin.includes(Scope::CodeWrite)); | |
| ⋯ | |||
| 1725 | 1746 | } | |
| 1726 | 1747 | ||
| 1727 | 1748 | #[test] | |
| 1749 | + | fn the_agent_preset_creates_repositories_and_changes_none() { | |
| 1750 | + | let agent = Preset::Agent.scopes().unwrap(); | |
| 1751 | + | assert!(agent.contains(&Scope::RepoCreate)); | |
| 1752 | + | assert!(!agent.contains(&Scope::RepoWrite) && !agent.contains(&Scope::RepoAdmin)); | |
| 1753 | + | assert!(oauth_default().contains(&Scope::RepoCreate)); | |
| 1754 | + | assert_eq!(Scope::parse("repo:create"), Some(Scope::RepoCreate)); | |
| 1755 | + | assert_eq!(Scope::RepoCreate.level(), Level::Create); | |
| 1756 | + | assert!(!Scope::RepoCreate.dangerous()); | |
| 1757 | + | // Creating needs repo:create, which repo:write still gives. | |
| 1758 | + | assert_eq!(scope_for("create_repo"), Some(Scope::RepoCreate)); | |
| 1759 | + | let creator = token(&agent); | |
| 1760 | + | assert!(decide(&creator, "create_repo", &json!({ "name": "web" })).allowed); | |
| 1761 | + | assert!(decide(&token(&[Scope::RepoWrite]), "create_repo", &json!({ "name": "web" })).allowed); | |
| 1762 | + | assert!(!decide(&token(&[Scope::RepoRead]), "create_repo", &json!({ "name": "web" })).allowed); | |
| 1763 | + | // And nothing that changes or deletes what is there. | |
| 1764 | + | for op in [ | |
| 1765 | + | "update_repo", | |
| 1766 | + | "update_repo_settings", | |
| 1767 | + | "delete_branch", | |
| 1768 | + | "rename_branch", | |
| 1769 | + | "delete_release", | |
| 1770 | + | "fork_repo", | |
| 1771 | + | "set_repo_visibility", | |
| 1772 | + | "delete_repo", | |
| 1773 | + | "transfer_repo", | |
| 1774 | + | ] { | |
| 1775 | + | assert!(!decide(&creator, op, &json!({ "repo": "acme/web" })).allowed, "{op}"); | |
| 1776 | + | } | |
| 1777 | + | // As a permission it is one level of repositories. | |
| 1778 | + | let asked = std::collections::BTreeMap::from([("repo".to_owned(), "create".to_owned())]); | |
| 1779 | + | assert_eq!(resolve_permissions(&asked, true), Ok(vec![Scope::RepoCreate])); | |
| 1780 | + | assert_eq!(top_scopes(&[Scope::RepoRead, Scope::RepoCreate]), vec![Scope::RepoCreate]); | |
| 1781 | + | } | |
| 1782 | + | ||
| 1783 | + | #[test] | |
| 1728 | 1784 | fn checks_are_reported_with_checks_write_which_ci_gets() { | |
| 1729 | 1785 | assert_eq!(scope_for("create_check_run"), Some(Scope::ChecksWrite)); | |
| 1730 | 1786 | assert_eq!(scope_for("create_commit_status"), Some(Scope::ChecksWrite)); | |
| 4162 | 4162 | path: "/repos", | |
| 4163 | 4163 | tag: "Repositories", | |
| 4164 | 4164 | summary: "Create a repository", | |
| 4165 | − | scope: Some("repo:write"), | |
| 4165 | + | scope: Some("repo:create"), | |
| 4166 | 4166 | params: &[], | |
| 4167 | 4167 | body: &[ | |
| 4168 | 4168 | Field { name: "description", kind: "string", required: false, values: &[] }, |
| 35 | 35 | | "models" | |
| 36 | 36 | | "artifacts"; | |
| 37 | 37 | ||
| 38 | − | export type ScopeLevel = "read" | "write" | "run" | "delete" | "admin"; | |
| 38 | + | export type ScopeLevel = "read" | "create" | "write" | "run" | "delete" | "admin"; | |
| 39 | 39 | ||
| 40 | 40 | /** Every scope, grouped by resource, least first. */ | |
| 41 | 41 | export const SCOPES = [ | |
| 42 | 42 | { scope: "repo:read", description: "See repositories, their settings, labels, timelines, releases, languages, contributors and security alerts, and search" }, | |
| 43 | + | { scope: "repo:create", description: "Create repositories, and nothing that changes or deletes one" }, | |
| 43 | 44 | { scope: "repo:write", description: "Create repositories, create, rename and delete branches, change how pull requests merge and publish releases" }, | |
| 44 | 45 | { scope: "repo:admin", description: "Rename, archive, transfer, delete or change who can see a repository, change its rulesets, and dismiss security alerts" }, | |
| 45 | 46 | { scope: "code:read", description: "Clone and fetch private repositories with git" }, | |
| ⋯ | |||
| 152 | 153 | { resource: "models", label: "AI Gateway", group: "workspace" }, | |
| 153 | 154 | ]; | |
| 154 | 155 | ||
| 155 | − | const LEVEL_ORDER: Record<ScopeLevel, number> = { read: 0, write: 1, run: 2, delete: 3, admin: 4 }; | |
| 156 | + | const LEVEL_ORDER: Record<ScopeLevel, number> = { read: 0, create: 1, write: 2, run: 3, delete: 4, admin: 5 }; | |
| 156 | 157 | ||
| 157 | 158 | export function scopeResource(scope: Scope): ScopeResource { | |
| 158 | 159 | return scope.split(":")[0] as ScopeResource; | |
| ⋯ | |||
| 254 | 255 | "repo:read", "code:read", "security:read", "packages:read", "issues:read", "pull_requests:read", "workflows:read", "checks:read", "deployments:read", "observability:read", "account:read", "notifications:read", "workspace:read", "billing:read", "access:read", "webhooks:read", "secrets:read", "runners:read", "artifacts:read", | |
| 255 | 256 | ] as const, | |
| 256 | 257 | agent: [ | |
| 257 | − | "repo:read", "code:read", "code:write", "security:read", "packages:read", "issues:read", "issues:write", "pull_requests:read", "pull_requests:write", "workflows:read", "checks:read", "deployments:read", "observability:read", "account:read", "notifications:read", "notifications:write", "workspace:read", "billing:read", "access:read", "webhooks:read", "secrets:read", "artifacts:read", | |
| 258 | + | "repo:read", "repo:create", "code:read", "code:write", "security:read", "packages:read", "issues:read", "issues:write", "pull_requests:read", "pull_requests:write", "workflows:read", "checks:read", "deployments:read", "observability:read", "account:read", "notifications:read", "notifications:write", "workspace:read", "billing:read", "access:read", "webhooks:read", "secrets:read", "artifacts:read", | |
| 258 | 259 | ] as const, | |
| 259 | 260 | ci: [ | |
| 260 | 261 | "repo:read", "code:read", "code:write", "packages:read", "packages:write", "workflows:read", "workflows:write", "checks:read", "checks:write", "deployments:read", "deployments:write", | |
| ⋯ | |||
| 266 | 267 | ||
| 267 | 268 | export const PRESETS: { id: PresetId; label: string; description: string }[] = [ | |
| 268 | 269 | { id: "read_only", label: "Read only", description: "Read everything you can read; change nothing." }, | |
| 269 | − | { id: "agent", label: "Agent", description: "Read everything, work on issues and pull requests, and push code." }, | |
| 270 | + | { id: "agent", label: "Agent", description: "Read everything, work on issues and pull requests, push code, and create repositories." }, | |
| 270 | 271 | { id: "ci", label: "CI", description: "Clone and push code, push and pull packages, run workflows, and report checks and deployments." }, | |
| 271 | 272 | { id: "full", label: "Full access", description: "Everything you can do, including deleting repositories and changing who has access." }, | |
| 272 | 273 | ]; | |
| ⋯ | |||
| 416 | 417 | ["list_deleted_repos", "repo:read"], | |
| 417 | 418 | ["list_security_alerts", "repo:read"], | |
| 418 | 419 | ["get_codeowners_errors", "repo:read"], | |
| 419 | − | ["create_repo", "repo:write"], | |
| 420 | + | ["create_repo", "repo:create"], | |
| 420 | 421 | ["update_repo", "repo:write"], | |
| 421 | 422 | ["update_project", "repo:write"], | |
| 422 | 423 | ["update_repo_settings", "repo:write"], | |
| ⋯ | |||
| 732 | 733 | * own (see `DANGEROUS_SCOPES`). Every other scope is in exactly one group. | |
| 733 | 734 | */ | |
| 734 | 735 | export const SCOPE_GROUPS: { id: string; label: string; scopes: Scope[] }[] = [ | |
| 735 | − | { id: "code", label: "Repositories & code", scopes: ["repo:read", "repo:write", "code:read", "code:write"] }, | |
| 736 | + | { id: "code", label: "Repositories & code", scopes: ["repo:read", "repo:create", "repo:write", "code:read", "code:write"] }, | |
| 736 | 737 | { id: "security", label: "Security", scopes: ["security:read", "security:write"] }, | |
| 737 | 738 | { id: "packages", label: "Packages", scopes: ["packages:read", "packages:write"] }, | |
| 738 | 739 | { id: "work", label: "Issues & pull requests", scopes: ["issues:read", "issues:write", "pull_requests:read", "pull_requests:write"] }, | |
Comments
Sign in to comment.