diff --git a/content/copilot/how-tos/configure-custom-instructions/add-repository-instructions.md b/content/copilot/how-tos/configure-custom-instructions-in-your-ide/add-repository-instructions-in-your-ide.md similarity index 62% rename from content/copilot/how-tos/configure-custom-instructions/add-repository-instructions.md rename to content/copilot/how-tos/configure-custom-instructions-in-your-ide/add-repository-instructions-in-your-ide.md index 61f202c0d9dd..a1f81c1adf81 100644 --- a/content/copilot/how-tos/configure-custom-instructions/add-repository-instructions.md +++ b/content/copilot/how-tos/configure-custom-instructions-in-your-ide/add-repository-instructions-in-your-ide.md @@ -1,200 +1,14 @@ --- -title: Adding repository custom instructions for GitHub Copilot -shortTitle: Add repository instructions +title: Adding repository custom instructions for GitHub Copilot in your IDE +shortTitle: Add repository instructions in your IDE intro: 'Create repository custom instructions files that give {% data variables.product.prodname_copilot_short %} additional context on how to understand your project and how to build, test and validate its changes.' versions: feature: copilot contentType: how-tos -redirect_from: - - /copilot/customizing-copilot/adding-custom-instructions-for-github-copilot - - /copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot - - /copilot/how-tos/custom-instructions/adding-repository-custom-instructions-for-github-copilot - - /copilot/how-tos/custom-instructions/add-repository-instructions - - /copilot/how-tos/use-copilot-agents/request-a-code-review/configure-coding-guidelines category: - Configure Copilot --- - - -{% webui %} - -This version of this article is for using repository custom instructions on the {% data variables.product.github %} website. Click the tabs above for information on using custom instructions in other environments. - -{% data reusables.copilot.repository-custom-instructions-about %} - -{% data reusables.copilot.repository-custom-instructions-prerequisites %} - -* For {% data variables.copilot.copilot_code-review_short %}, your personal choice of whether to use custom instructions must be set to enabled. This is enabled by default. See [Enabling or disabling repository custom instructions](#enabling-or-disabling-custom-instructions-for-copilot-code-review) later in this article. - -## Creating custom instructions - -{% data variables.product.prodname_copilot_short %} on {% data variables.product.github %} supports three types of repository custom instructions. For details of which {% data variables.product.prodname_copilot %} features support these types of instructions, see [AUTOTITLE](/copilot/concepts/prompting/response-customization?tool=webui#support-for-repository-custom-instructions). - -* **Repository-wide custom instructions** apply to all requests made in the context of a repository. - - These are specified in a `copilot-instructions.md` file in the `.github` directory of the repository. See [Creating repository-wide custom instructions](#creating-repository-wide-custom-instructions-2). - -* **Path-specific custom instructions** apply to requests made in the context of files that match a specified path. - - These are specified in one or more `NAME.instructions.md` files within or below the `.github/instructions` directory in the repository. See [Creating path-specific custom instructions](#creating-path-specific-custom-instructions-2). - - If the path you specify matches a file that {% data variables.product.prodname_copilot_short %} is working on, and a repository-wide custom instructions file also exists, then the instructions from both files are used. - -* **Agent instructions** are used by AI agents. - - {% data reusables.copilot.custom-instructions-agents %} - - Alternatively, you can use a single `CLAUDE.md` or `GEMINI.md` file stored in the root of the repository. - -## Creating repository-wide custom instructions - -You can create your own custom instructions file from scratch. See [Writing your own copilot-instructions.md file](#writing-your-own-copilot-instructionsmd-file). Alternatively, you can ask {% data variables.copilot.copilot_cloud_agent %} to generate one for you. - -### Asking {% data variables.copilot.copilot_cloud_agent %} to generate a `copilot-instructions.md` file - -1. Navigate to the agents tab at [github.com/copilot/agents](https://github.com/copilot/agents?ref_product=copilot&ref_type=engagement&ref_style=text). - - You can also reach this page by clicking the **{% octicon "copilot" aria-label="Copilot icon" %}** button next to the search bar on any page on {% data variables.product.github %}, then selecting **Agents** from the sidebar. - -1. Using the dropdown menu in the prompt field, select the repository you want {% data variables.product.prodname_copilot_short %} to generate custom instructions for. -1. Copy the following prompt and paste it into the prompt field, customizing it if needed: - - ```markdown copy - Your task is to "onboard" this repository to Copilot cloud agent by adding a .github/copilot-instructions.md file in the repository that contains information describing how a cloud agent seeing it for the first time can work most efficiently. - - You will do this task only one time per repository and doing a good job can SIGNIFICANTLY improve the quality of the agent's work, so take your time, think carefully, and search thoroughly before writing the instructions. - - - - Reduce the likelihood of a cloud agent pull request getting rejected by the user due to - generating code that fails the continuous integration build, fails a validation pipeline, or - having misbehavior. - - Minimize bash command and build failures. - - Allow the agent to complete its task more quickly by minimizing the need for exploration using grep, find, str_replace_editor, and code search tools. - - - - - Instructions must be no longer than 2 pages. - - Instructions must not be task specific. - - - - - Add the following high level details about the codebase to reduce the amount of searching the agent has to do to understand the codebase each time: - - - - A summary of what the repository does. - - High level repository information, such as the size of the repo, the type of the project, the languages, frameworks, or target runtimes in use. - - - Add information about how to build and validate changes so the agent does not need to search and find it each time. - - - - For each of bootstrap, build, test, run, lint, and any other scripted step, document the sequence of steps to take to run it successfully as well as the versions of any runtime or build tools used. - - Each command should be validated by running it to ensure that it works correctly as well as any preconditions and postconditions. - - Try cleaning the repo and environment and running commands in different orders and document errors and misbehavior observed as well as any steps used to mitigate the problem. - - Run the tests and document the order of steps required to run the tests. - - Make a change to the codebase. Document any unexpected build issues as well as the workarounds. - - Document environment setup steps that seem optional but that you have validated are actually required. - - Document the time required for commands that failed due to timing out. - - When you find a sequence of commands that work for a particular purpose, document them in detail. - - Use language to indicate when something should always be done. For example: "always run npm install before building". - - Record any validation steps from documentation. - - - List key facts about the layout and architecture of the codebase to help the agent find where to make changes with minimal searching. - - - - A description of the major architectural elements of the project, including the relative paths to the main project files, the location - of configuration files for linting, compilation, testing, and preferences. - - A description of the checks run prior to check in, including any GitHub workflows, continuous integration builds, or other validation pipelines. - - Document the steps so that the agent can replicate these itself. - - Any explicit validation steps that the agent can consider to have further confidence in its changes. - - Dependencies that aren't obvious from the layout or file structure. - - Finally, fill in any remaining space with detailed lists of the following, in order of priority: the list of files in the repo root, the - contents of the README, the contents of any key source files, the list of files in the next level down of directories, giving priority to the more structurally important and snippets of code from key source files, such as the one containing the main method. - - - - - - Perform a comprehensive inventory of the codebase. Search for and view: - - README.md, CONTRIBUTING.md, and all other documentation files. - - Search the codebase for build steps and indications of workarounds like 'HACK', 'TODO', etc. - - All scripts, particularly those pertaining to build and repo or environment setup. - - All build and actions pipelines. - - All project files. - - All configuration and linting files. - - For each file: - - think: are the contents or the existence of the file information that the cloud agent will need to implement, build, test, validate, or demo a code change? - - If yes: - - Document the command or information in detail. - - Explicitly indicate which commands work and which do not and the order in which commands should be run. - - Document any errors encountered as well as the steps taken to workaround them. - - Document any other steps or information that the agent can use to reduce time spent exploring or trying and failing to run bash commands. - - Finally, explicitly instruct the agent to trust the instructions and only perform a search if the information in the instructions is incomplete or found to be in error. - - - Document any errors encountered as well as the steps taken to work-around them. - -1. Click **{% octicon "paper-airplane" aria-label="Start task" %}** or press Enter. - -{% data variables.product.prodname_copilot_short %} will start a new session, which will appear in the list below the prompt box. {% data variables.product.prodname_copilot_short %} will create a draft pull request, write your custom instructions, push them to the branch, then add you as a reviewer when it has finished, triggering a notification. - -### Writing your own `copilot-instructions.md` file - -1. In the root of your repository, create a file named `.github/copilot-instructions.md`. - - Create the `.github` directory if it does not already exist. - -1. Add natural language instructions to the file, in Markdown format. - - Whitespace between instructions is ignored, so the instructions can be written as a single paragraph, each on a new line, or separated by blank lines for legibility. - -> [!TIP] -> The first time you create a pull request in a given repository with {% data variables.copilot.copilot_cloud_agent %}, {% data variables.product.prodname_copilot_short %} will leave a comment with a link to automatically generate custom instructions for the repository. - -## Creating path-specific custom instructions - -> [!NOTE] -> Currently, on {% data variables.product.prodname_dotcom_the_website %}, path-specific custom instructions are only supported for {% data variables.copilot.copilot_cloud_agent %} and {% data variables.copilot.copilot_code-review_short %}. - -{% data reusables.copilot.custom-instructions-path %} - -{% data reusables.copilot.custom-instructions-note %} - -In {% data variables.copilot.copilot_chat_short %} ([github.com/copilot](https://github.com/copilot)), you can start a conversation that uses repository custom instructions by adding, as an attachment, the repository that contains the instructions file. - -Whenever repository custom instructions are used by {% data variables.copilot.copilot_chat_short %}, the instructions file is added as a reference for the response that's generated. To find out whether repository custom instructions were used, expand the list of references at the top of a chat response in the Chat panel and check whether the `.github/copilot-instructions.md` file is listed. - -![Screenshot of an expanded References list, showing the 'copilot-instructions.md' file highlighted with a dark orange outline.](/assets/images/help/copilot/custom-instructions-ref-in-github.png) - -You can click the reference to open the file. - -> [!NOTE] -> * {% data reusables.copilot.custom-instructions-chat-precedence %} -> * {% data reusables.copilot.custom-instructions-conflict %} - -## Enabling or disabling custom instructions for {% data variables.copilot.copilot_code-review_short %} - -Custom instructions are enabled for {% data variables.copilot.copilot_code-review_short %} by default but you can disable, or re-enable, them in the repository settings on {% data variables.product.prodname_dotcom_the_website %}. This applies to {% data variables.product.prodname_copilot_short %}'s use of custom instructions for all code reviews it performs in this repository. - -{% data reusables.repositories.navigate-to-repo %} -{% data reusables.repositories.sidebar-settings %} -1. In the "Code & automation" section of the sidebar, click **{% octicon "copilot" aria-hidden="true" aria-label="copilot" %} {% data variables.product.prodname_copilot_short %}**, then **Code review**. -1. Toggle the “Use custom instructions when reviewing pull requests” option on or off. - -> [!NOTE] -> {% data reusables.copilot.code-review.custom-instructions-branch %} - -## Further reading - -* [AUTOTITLE](/copilot/reference/custom-instructions-support) -* [AUTOTITLE](/copilot/tutorials/customization-library/custom-instructions)—a curated collection of examples -* [AUTOTITLE](/copilot/tutorials/use-custom-instructions) - -{% endwebui %} - - - {% vscode %} @@ -205,7 +19,7 @@ This version of this article is for using repository custom instructions and pro {% data reusables.copilot.repository-custom-instructions-prerequisites %} -* Custom instructions must be enabled. This feature is enabled by default. See [Enabling or disabling repository custom instructions](#enabling-or-disabling-repository-custom-instructions-1) later in this article. +* Custom instructions must be enabled. This feature is enabled by default. See [Enabling or disabling repository custom instructions](#enabling-or-disabling-repository-custom-instructions) later in this article. ## Creating custom instructions @@ -215,11 +29,11 @@ This version of this article is for using repository custom instructions and pro * **Repository-wide custom instructions**, which apply to all requests made in the context of a repository. - These are specified in a `copilot-instructions.md` file in the `.github` directory of the repository. See [Creating repository-wide custom instructions](#creating-repository-wide-custom-instructions-1). + These are specified in a `copilot-instructions.md` file in the `.github` directory of the repository. See [Creating repository-wide custom instructions](#creating-repository-wide-custom-instructions). * **Path-specific custom instructions**, which apply to requests made in the context of files that match a specified path. - These are specified in one or more `NAME.instructions.md` files within or below the `.github/instructions` directory in the repository. See [Creating path-specific custom instructions](#creating-path-specific-custom-instructions-1). + These are specified in one or more `NAME.instructions.md` files within or below the `.github/instructions` directory in the repository. See [Creating path-specific custom instructions](#creating-path-specific-custom-instructions). If the path you specify matches a file that {% data variables.product.prodname_copilot_short %} is working on, and a repository-wide custom instructions file also exists, then the instructions from both files are used. @@ -324,11 +138,11 @@ This version of this article is for using repository custom instructions and pro * **Repository-wide custom instructions**, which apply to all requests made in the context of a repository. - These are specified in a `copilot-instructions.md` file in the `.github` directory of the repository. See [Creating repository-wide custom instructions](#creating-repository-wide-custom-instructions-2). + These are specified in a `copilot-instructions.md` file in the `.github` directory of the repository. See [Creating repository-wide custom instructions](#creating-repository-wide-custom-instructions-1). * **Path-specific custom instructions**, which apply to requests made in the context of files that match a specified path. - These are specified in one or more `NAME.instructions.md` files within or below the `.github/instructions` directory in the repository. See [Creating path-specific custom instructions](#creating-path-specific-custom-instructions-2). + These are specified in one or more `NAME.instructions.md` files within or below the `.github/instructions` directory in the repository. See [Creating path-specific custom instructions](#creating-path-specific-custom-instructions-1). If the path you specify matches a file that {% data variables.product.prodname_copilot_short %} is working on, and a repository-wide custom instructions file also exists, then the instructions from both files are used. diff --git a/content/copilot/how-tos/configure-custom-instructions/index.md b/content/copilot/how-tos/configure-custom-instructions-in-your-ide/index.md similarity index 66% rename from content/copilot/how-tos/configure-custom-instructions/index.md rename to content/copilot/how-tos/configure-custom-instructions-in-your-ide/index.md index e7003aa1e8af..fc011f1dc7e9 100644 --- a/content/copilot/how-tos/configure-custom-instructions/index.md +++ b/content/copilot/how-tos/configure-custom-instructions-in-your-ide/index.md @@ -5,10 +5,6 @@ intro: 'Learn how to give {% data variables.product.prodname_copilot %} persiste versions: feature: copilot children: - - /add-personal-instructions - - /add-repository-instructions - - /add-organization-instructions -redirect_from: - - /copilot/how-tos/custom-instructions + - /add-repository-instructions-in-your-ide contentType: how-tos --- diff --git a/content/copilot/how-tos/copilot-cli/index.md b/content/copilot/how-tos/copilot-cli/index.md index bb40b56de0c1..2d54e41dc6c9 100644 --- a/content/copilot/how-tos/copilot-cli/index.md +++ b/content/copilot/how-tos/copilot-cli/index.md @@ -25,6 +25,7 @@ children: - /use-copilot-cli-agents - /administer-copilot-cli-for-your-enterprise - /speed-up-task-completion + - /manage-pull-requests - /roll-back-changes - /chronicle - /content/copilot/concepts/agents/copilot-cli/about-copilot-cli diff --git a/content/copilot/how-tos/copilot-cli/manage-pull-requests.md b/content/copilot/how-tos/copilot-cli/manage-pull-requests.md new file mode 100644 index 000000000000..224d98b09525 --- /dev/null +++ b/content/copilot/how-tos/copilot-cli/manage-pull-requests.md @@ -0,0 +1,213 @@ +--- +title: 'Managing pull requests with the /pr command' +shortTitle: Manage pull requests +intro: 'Use the `/pr` slash command to view, create, and fix pull requests directly from {% data variables.copilot.copilot_cli_short %}.' +versions: + feature: copilot +contentType: how-tos +category: + - Author and optimize with Copilot # Copilot discovery page + - Build with Copilot CLI # Copilot CLI bespoke page +docsTeamMetrics: + - copilot-cli +--- + +## Overview + +The `/pr` slash command lets you manage the full pull request lifecycle without leaving your terminal. You can check pull request status, create new pull requests, and fix common issues such as review feedback, merge conflicts, and CI failures. + +## Prerequisite + +You must be working in a Git repository that is hosted on {% data variables.product.prodname_dotcom %}. + +## Subcommands + +The `/pr` slash command has several subcommands that you can use to perform different actions on your pull requests. + +> [!NOTE] +> All `/pr` subcommands relate to the current branch—for example, fixing failing CI checks for the pull request associated with the current branch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Use this subcommandTo do thisRequires an existing PRMay commit and push
/pr or /pr viewShow the status of the pull request for the current branch. Find out moreYesNo
/pr view webOpen the pull request in your browser. Find out moreYesNo
/pr createCreate or update a pull request. Find out moreNoYes
/pr fix feedbackAddress review comments on the pull request. Find out moreYesYes
/pr fix conflictsSync the branch with the base branch and resolve conflicts. Find out moreYesYes
/pr fix ciDiagnose and fix failing CI checks. Find out moreYesYes
/pr fix or /pr fix allRun all three fix phases in order: feedback, conflicts, then CI. Find out moreYesYes
/pr autoCreate a pull request if needed, then loop through all fix phases until all pull request checks are passing. Find out moreNoYes
+ +Subcommands that commit and push changes will prompt you for permission before performing potentially destructive actions, unless you have pre-allowed those tools. For more information, see [AUTOTITLE](/copilot/how-tos/copilot-cli/allowing-tools). + +## Viewing pull request status + +To check the status of the pull request associated with your current branch, enter the following in an interactive session: + +```copilot copy +/pr +``` + +### Opening the pull request in your browser + +If you want to view the pull request on {% data variables.product.prodname_dotcom_the_website %} instead of in the terminal, enter: + +```copilot copy +/pr view web +``` + +This opens the pull request URL in your default browser. + +## Creating a pull request + +To create a pull request from your current branch, enter: + +```copilot copy +/pr create +``` + +{% data variables.product.prodname_copilot_short %} ensures that all local commits are pushed to the remote branch, then creates the pull request. If a pull request template exists in the repository, {% data variables.product.prodname_copilot_short %} follows it when generating the pull request title and description. + +If a pull request already exists for the current branch, `/pr create` updates the existing pull request instead of creating a new one. + +### Adding instructions for pull request creation + +You can append instructions after `/pr create` to guide {% data variables.product.prodname_copilot_short %}. For example: + +```copilot copy +/pr create prefix the PR title 'Project X: ' +``` + +## Fixing review feedback + +To have {% data variables.product.prodname_copilot_short %} read and address review comments on your pull request, enter: + +```copilot copy +/pr fix feedback +``` + +{% data variables.product.prodname_copilot_short %} fetches all review comment threads on the pull request, determines what changes are requested, applies the changes to your codebase, and commits and pushes the fixes. Actionable code change requests are prioritized over conversational comments. + +## Resolving merge conflicts + +To sync your branch with the base branch and resolve any merge conflicts, enter: + +```copilot copy +/pr fix conflicts +``` + +{% data variables.product.prodname_copilot_short %} fetches the latest base branch, syncs your branch, resolves any conflicts, and pushes the result. + +### Choosing a merge strategy + +When resolving conflicts, {% data variables.copilot.copilot_cli_short %} needs to know whether to use a rebase or merge strategy. If you have not configured a preference, {% data variables.product.prodname_copilot_short %} prompts you to choose when you run a command that involves conflict resolution. + +To set a default merge strategy so that you are not prompted each time, add the `mergeStrategy` setting to your configuration file. You can set this in your personal user settings for {% data variables.product.prodname_copilot_short %}, or in the repository settings. + +* **User settings**: Add `"mergeStrategy": "rebase"` or `"mergeStrategy": "merge"` to your user-level configuration file (typically `~/.copilot/config.json`). +* **Repository settings**: Add the same setting to `.github/copilot/settings.json` in your repository root. + +## Fixing CI failures + +To have {% data variables.product.prodname_copilot_short %} diagnose and fix failing CI checks, enter: + +```copilot copy +/pr fix ci +``` + +{% data variables.product.prodname_copilot_short %} identifies the failing CI jobs, analyzes the logs to determine root causes, applies targeted fixes, and pushes them. It then re-checks the CI status and repeats the process until the checks pass or it determines that further progress is not possible. + +If failures are unrelated to your branch changes, {% data variables.product.prodname_copilot_short %} notes this clearly so you can take appropriate action. + +You can append additional context to help {% data variables.product.prodname_copilot_short %} focus on specific failures. For example: + +```copilot copy +/pr fix ci focus on test failures +``` + +## Fixing all issues at once + +To address all outstanding issues on a pull request in a single command, enter: + +```copilot copy +/pr fix +``` + +This runs three phases in order: + +1. **Review feedback** — Addresses all review comments. +1. **Conflicts** — Syncs the branch with the base branch and resolves any conflicts. +1. **CI failures** — Diagnoses and fixes failing CI checks. + +## Automating the full pull request workflow + +To have {% data variables.product.prodname_copilot_short %} manage the entire pull request process from creation to a fully green state, enter: + +```copilot copy +/pr auto +``` + +If no pull request exists for the current branch, {% data variables.product.prodname_copilot_short %} creates one first. It then loops through the fix phases—review feedback, conflicts, and CI—repeating until there are no more review comments, no conflicts, and all CI checks pass. + +You can append instructions to guide the pull request creation. For example: + +```copilot copy +/pr auto include migration notes in the description +``` + +## Further reading + +* [AUTOTITLE](/copilot/how-tos/copilot-cli/allowing-tools) +* [AUTOTITLE](/copilot/reference/copilot-cli-reference/cli-command-reference) diff --git a/content/copilot/how-tos/configure-custom-instructions/add-organization-instructions.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-organization-instructions.md similarity index 96% rename from content/copilot/how-tos/configure-custom-instructions/add-organization-instructions.md rename to content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-organization-instructions.md index c50ec01381c6..1993941357c2 100644 --- a/content/copilot/how-tos/configure-custom-instructions/add-organization-instructions.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-organization-instructions.md @@ -10,6 +10,7 @@ redirect_from: - /copilot/customizing-copilot/adding-organization-custom-instructions-for-github-copilot - /copilot/how-tos/custom-instructions/adding-organization-custom-instructions-for-github-copilot - /copilot/how-tos/custom-instructions/add-organization-instructions + - /copilot/how-tos/configure-custom-instructions/add-organization-instructions contentType: how-tos category: - Configure Copilot diff --git a/content/copilot/how-tos/configure-custom-instructions/add-personal-instructions.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-personal-instructions.md similarity index 73% rename from content/copilot/how-tos/configure-custom-instructions/add-personal-instructions.md rename to content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-personal-instructions.md index b83a093c0186..d7787645f8c3 100644 --- a/content/copilot/how-tos/configure-custom-instructions/add-personal-instructions.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-personal-instructions.md @@ -8,6 +8,7 @@ redirect_from: - /copilot/customizing-copilot/adding-personal-custom-instructions-for-github-copilot - /copilot/how-tos/custom-instructions/adding-personal-custom-instructions-for-github-copilot - /copilot/how-tos/custom-instructions/add-personal-instructions + - /copilot/how-tos/configure-custom-instructions/add-personal-instructions contentType: how-tos category: - Configure Copilot @@ -15,11 +16,11 @@ category: {% data reusables.copilot.personal-instructions-note %} -You can customize {% data variables.copilot.copilot_chat %} responses in several ways. For an overview, see [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses?tool=webui). +You can layer multiple types of custom instructions to shape {% data variables.copilot.copilot_chat %} responses. For an overview, see [AUTOTITLE](/copilot/concepts/about-customizing-github-copilot-chat-responses?tool=webui). ## About personal custom instructions for {% data variables.copilot.copilot_chat_short %} -Add custom instructions to receive personalized chat responses. Your instructions apply to all your conversations on the {% data variables.product.github %} website. Custom instructions let you specify preferences such as your preferred language or response style. +Personal custom instructions apply to every conversation you have on the {% data variables.product.github %} website, so {% data variables.product.prodname_copilot_short %} always responds in your preferred language, tone, and style. Examples of instructions you can add: @@ -27,13 +28,13 @@ Examples of instructions you can add: * `Use a helpful, collegial tone. Keep explanations brief, but provide enough context to understand the code.` * `Always provide examples in TypeScript.` -> [!NOTE] -> * {% data reusables.copilot.custom-instructions-chat-precedence %} -> * {% data reusables.copilot.custom-instructions-conflict %} +{% data reusables.copilot.custom-instructions-chat-precedence %} + +{% data reusables.copilot.custom-instructions-conflict %} ## Adding personal custom instructions -To add personal custom instructions in {% data variables.copilot.copilot_chat_short %} on {% data variables.product.github %}: +To add personal custom instructions on {% data variables.product.github %}: 1. Open [{% data variables.copilot.copilot_chat_short %}](https://github.com/copilot?ref_product=copilot&ref_type=engagement&ref_style=text). 1. In the bottom left corner, click your profile picture. Then click **{% octicon "note" aria-hidden="true" aria-label="note" %} Personal instructions**. @@ -46,7 +47,7 @@ To add personal custom instructions in {% data variables.copilot.copilot_chat_sh When you select a template, placeholder text appears. Replace placeholders like `{format}` with your preferences. 1. Click **Save**. -Your instructions are now active. They will remain active until you change or remove them. +Your instructions are now active and remain active until you change or remove them. {% note %} diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions.md new file mode 100644 index 000000000000..86dba096a540 --- /dev/null +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions.md @@ -0,0 +1,189 @@ +--- +title: Adding repository custom instructions for GitHub Copilot +shortTitle: Add repository instructions +intro: 'Create repository custom instructions files that give {% data variables.product.prodname_copilot_short %} additional context on how to understand your project and how to build, test and validate its changes.' +versions: + feature: copilot +contentType: how-tos +redirect_from: + - /copilot/customizing-copilot/adding-custom-instructions-for-github-copilot + - /copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot + - /copilot/how-tos/custom-instructions/adding-repository-custom-instructions-for-github-copilot + - /copilot/how-tos/custom-instructions/add-repository-instructions + - /copilot/how-tos/use-copilot-agents/request-a-code-review/configure-coding-guidelines + - /copilot/how-tos/configure-custom-instructions/add-repository-instructions +category: + - Configure Copilot +--- + +## Introduction + +Repository custom instructions let you provide {% data variables.product.prodname_copilot_short %} with repository-specific guidance and preferences on {% data variables.product.github %}. To find out how to set up custom instructions in an IDE, see [AUTOTITLE](/copilot/how-tos/configure-custom-instructions-in-your-ide/add-repository-instructions-in-your-ide). For more information about custom instructions, see [AUTOTITLE](/copilot/concepts/prompting/response-customization). + +{% data reusables.copilot.repository-custom-instructions-prerequisites %} + +* For {% data variables.copilot.copilot_code-review_short %}, your personal choice of whether to use custom instructions must be set to enabled. This is enabled by default. See [Enabling or disabling repository custom instructions](#enabling-or-disabling-custom-instructions-for-copilot-code-review) later in this article. + +## Creating custom instructions + +{% data variables.product.prodname_copilot_short %} on {% data variables.product.github %} supports three types of repository custom instructions. For details of which {% data variables.product.prodname_copilot %} features support these types of instructions, see [AUTOTITLE](/copilot/concepts/prompting/response-customization?tool=webui#support-for-repository-custom-instructions). + +* **Repository-wide custom instructions** apply to all requests made in the context of a repository. + + These are specified in a `copilot-instructions.md` file in the `.github` directory of the repository. See [Creating repository-wide custom instructions](#creating-repository-wide-custom-instructions). + +* **Path-specific custom instructions** apply to requests made in the context of files that match a specified path. + + These are specified in one or more `NAME.instructions.md` files within or below the `.github/instructions` directory in the repository. See [Creating path-specific custom instructions](#creating-path-specific-custom-instructions). + + If the path you specify matches a file that {% data variables.product.prodname_copilot_short %} is working on, and a repository-wide custom instructions file also exists, then the instructions from both files are used. + +* **Agent instructions** are used by AI agents. + + {% data reusables.copilot.custom-instructions-agents %} + + Alternatively, you can use a single `CLAUDE.md` or `GEMINI.md` file stored in the root of the repository. + +## Creating repository-wide custom instructions + +You can create your own custom instructions file from scratch. See [Writing your own copilot-instructions.md file](#writing-your-own-copilot-instructionsmd-file). Alternatively, you can ask {% data variables.copilot.copilot_cloud_agent %} to generate one for you. + +### Asking {% data variables.copilot.copilot_cloud_agent %} to generate a `copilot-instructions.md` file + +1. Go to the agents tab at [github.com/copilot/agents](https://github.com/copilot/agents?ref_product=copilot&ref_type=engagement&ref_style=text). + + You can also reach this page by clicking the **{% octicon "copilot" aria-label="Copilot icon" %}** button next to the search bar on any page on {% data variables.product.github %}, then selecting **Agents** from the sidebar. + +1. In the prompt field dropdown, select the repository you want {% data variables.product.prodname_copilot_short %} to generate custom instructions for. +1. Copy the following prompt and paste it into the prompt field, customizing it if needed: + + ```markdown copy + Your task is to "onboard" this repository to Copilot cloud agent by adding a .github/copilot-instructions.md file in the repository that contains information describing how a cloud agent seeing it for the first time can work most efficiently. + + You will do this task only one time per repository and doing a good job can SIGNIFICANTLY improve the quality of the agent's work, so take your time, think carefully, and search thoroughly before writing the instructions. + + + - Reduce the likelihood of a cloud agent pull request getting rejected by the user due to + generating code that fails the continuous integration build, fails a validation pipeline, or + having misbehavior. + - Minimize bash command and build failures. + - Allow the agent to complete its task more quickly by minimizing the need for exploration using grep, find, str_replace_editor, and code search tools. + + + + - Instructions must be no longer than 2 pages. + - Instructions must not be task specific. + + + + + Add the following high level details about the codebase to reduce the amount of searching the agent has to do to understand the codebase each time: + + + - A summary of what the repository does. + - High level repository information, such as the size of the repo, the type of the project, the languages, frameworks, or target runtimes in use. + + + Add information about how to build and validate changes so the agent does not need to search and find it each time. + + + - For each of bootstrap, build, test, run, lint, and any other scripted step, document the sequence of steps to take to run it successfully as well as the versions of any runtime or build tools used. + - Each command should be validated by running it to ensure that it works correctly as well as any preconditions and postconditions. + - Try cleaning the repo and environment and running commands in different orders and document errors and misbehavior observed as well as any steps used to mitigate the problem. + - Run the tests and document the order of steps required to run the tests. + - Make a change to the codebase. Document any unexpected build issues as well as the workarounds. + - Document environment setup steps that seem optional but that you have validated are actually required. + - Document the time required for commands that failed due to timing out. + - When you find a sequence of commands that work for a particular purpose, document them in detail. + - Use language to indicate when something should always be done. For example: "always run npm install before building". + - Record any validation steps from documentation. + + + List key facts about the layout and architecture of the codebase to help the agent find where to make changes with minimal searching. + + + - A description of the major architectural elements of the project, including the relative paths to the main project files, the location + of configuration files for linting, compilation, testing, and preferences. + - A description of the checks run prior to check in, including any GitHub workflows, continuous integration builds, or other validation pipelines. + - Document the steps so that the agent can replicate these itself. + - Any explicit validation steps that the agent can consider to have further confidence in its changes. + - Dependencies that aren't obvious from the layout or file structure. + - Finally, fill in any remaining space with detailed lists of the following, in order of priority: the list of files in the repo root, the + contents of the README, the contents of any key source files, the list of files in the next level down of directories, giving priority to the more structurally important and snippets of code from key source files, such as the one containing the main method. + + + + + - Perform a comprehensive inventory of the codebase. Search for and view: + - README.md, CONTRIBUTING.md, and all other documentation files. + - Search the codebase for build steps and indications of workarounds like 'HACK', 'TODO', etc. + - All scripts, particularly those pertaining to build and repo or environment setup. + - All build and actions pipelines. + - All project files. + - All configuration and linting files. + - For each file: + - think: are the contents or the existence of the file information that the cloud agent will need to implement, build, test, validate, or demo a code change? + - If yes: + - Document the command or information in detail. + - Explicitly indicate which commands work and which do not and the order in which commands should be run. + - Document any errors encountered as well as the steps taken to workaround them. + - Document any other steps or information that the agent can use to reduce time spent exploring or trying and failing to run bash commands. + - Finally, explicitly instruct the agent to trust the instructions and only perform a search if the information in the instructions is incomplete or found to be in error. + + - Document any errors encountered as well as the steps taken to work-around them. + +1. Click **{% octicon "paper-airplane" aria-label="Start task" %}** or press Enter. + +{% data variables.product.prodname_copilot_short %} will start a new session, which will appear in the list below the prompt box. {% data variables.product.prodname_copilot_short %} will create a draft pull request, write your custom instructions, push them to the branch, then add you as a reviewer when finished, triggering a notification. + +### Writing your own `copilot-instructions.md` file + +1. In the root of your repository, create a file named `.github/copilot-instructions.md`. + + Create the `.github` directory if it does not already exist. + +1. Add natural language instructions to the file, in Markdown format. + + Whitespace between instructions is ignored, so the instructions can be written as a single paragraph, each on a new line, or separated by blank lines for legibility. + +> [!TIP] +> The first time you create a pull request in a given repository with {% data variables.copilot.copilot_cloud_agent %}, {% data variables.product.prodname_copilot_short %} will leave a comment with a link to automatically generate custom instructions for the repository. + +## Creating path-specific custom instructions + +> [!NOTE] +> Currently, on {% data variables.product.prodname_dotcom_the_website %}, path-specific custom instructions are only supported for {% data variables.copilot.copilot_cloud_agent %} and {% data variables.copilot.copilot_code-review_short %}. + +{% data reusables.copilot.custom-instructions-path %} + +{% data reusables.copilot.custom-instructions-note %} + +In {% data variables.copilot.copilot_chat_short %} ([github.com/copilot](https://github.com/copilot)), you can start a conversation that uses repository custom instructions by adding, as an attachment, the repository that contains the instructions file. + +Whenever repository custom instructions are used by {% data variables.copilot.copilot_chat_short %}, the instructions file is added as a reference for the response that's generated. To find out whether repository custom instructions were used, expand the list of references at the top of a chat response in the Chat panel and check whether the `.github/copilot-instructions.md` file is listed. + +![Screenshot of an expanded References list, showing the 'copilot-instructions.md' file highlighted with a dark orange outline.](/assets/images/help/copilot/custom-instructions-ref-in-github.png) + +You can click the reference to open the file. + +> [!NOTE] +> * {% data reusables.copilot.custom-instructions-chat-precedence %} +> * {% data reusables.copilot.custom-instructions-conflict %} + +## Enabling or disabling custom instructions for {% data variables.copilot.copilot_code-review_short %} + +Custom instructions are enabled for {% data variables.copilot.copilot_code-review_short %} by default but you can disable, or re-enable, them in the repository settings on {% data variables.product.prodname_dotcom_the_website %}. This applies to {% data variables.product.prodname_copilot_short %}'s use of custom instructions for all code reviews it performs in this repository. + +{% data reusables.repositories.navigate-to-repo %} +{% data reusables.repositories.sidebar-settings %} +1. In the "Code & automation" section of the sidebar, click **{% octicon "copilot" aria-hidden="true" aria-label="copilot" %} {% data variables.product.prodname_copilot_short %}**, then **Code review**. +1. Toggle the “Use custom instructions when reviewing pull requests” option on or off. + +> [!NOTE] +> {% data reusables.copilot.code-review.custom-instructions-branch %} + +## Further reading + +* [AUTOTITLE](/copilot/reference/custom-instructions-support) +* [AUTOTITLE](/copilot/tutorials/customization-library/custom-instructions)—a curated collection of examples +* [AUTOTITLE](/copilot/tutorials/use-custom-instructions) diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/index.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/index.md new file mode 100644 index 000000000000..0be9ff003c2b --- /dev/null +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/index.md @@ -0,0 +1,15 @@ +--- +title: 'Add custom instructions for {% data variables.product.prodname_copilot_short %}' +shortTitle: Add custom instructions +intro: 'Give {% data variables.product.prodname_copilot %} persistent instructions to customize responses according to your needs.' +versions: + feature: copilot +children: + - /add-personal-instructions + - /add-repository-instructions + - /add-organization-instructions +redirect_from: + - /copilot/how-tos/configure-custom-instructions + - /copilot/how-tos/custom-instructions +contentType: how-tos +--- diff --git a/content/copilot/how-tos/provide-context/use-copilot-spaces/collaborate-with-others.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/collaborate-with-others.md similarity index 88% rename from content/copilot/how-tos/provide-context/use-copilot-spaces/collaborate-with-others.md rename to content/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/collaborate-with-others.md index 4829b4cf3bd6..13c2871b864b 100644 --- a/content/copilot/how-tos/provide-context/use-copilot-spaces/collaborate-with-others.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/collaborate-with-others.md @@ -1,7 +1,7 @@ --- title: Collaborating with others using GitHub Copilot Spaces shortTitle: Collaborate with others -intro: 'Learn how to share {% data variables.copilot.copilot_spaces %} to support collaboration and knowledge sharing.' +intro: 'Share {% data variables.copilot.copilot_spaces %} to support collaboration and knowledge sharing.' permissions: 'Anyone with a {% data variables.product.prodname_copilot_short %} license can use {% data variables.copilot.copilot_spaces_short %}.' versions: feature: copilot @@ -11,6 +11,7 @@ redirect_from: - /copilot/how-tos/context/copilot-spaces/collaborate-with-your-team - /copilot/how-tos/context/use-copilot-spaces/collaborate-with-your-team - /copilot/how-tos/provide-context/use-copilot-spaces/collaborate-with-your-team + - /copilot/how-tos/provide-context/use-copilot-spaces/collaborate-with-others contentType: how-tos category: - Author and optimize with Copilot @@ -29,7 +30,7 @@ category: * **System knowledge**: Create a space for a complex system or workflow (like authentication or CI pipelines) that other people can reference. * **Style guides or review checklists**: Document standards and examples in a space that {% data variables.product.prodname_copilot_short %} can reference when suggesting changes. -For example, a subject matter expert creates a space called “Accessibility Reviews” that includes your team’s internal accessibility checklist, product guidelines, and WCAG documentation. Developers can ask {% data variables.product.prodname_copilot_short %} questions directly in the space to ensure they're following the latest guidelines in their work. +For example, a subject matter expert creates a space called "Accessibility Reviews" that includes your team's internal accessibility checklist, product guidelines, and WCAG documentation. Developers can ask {% data variables.product.prodname_copilot_short %} questions directly in the space to ensure they're following the latest guidelines in their work. ## Sharing {% data variables.copilot.copilot_spaces_short %} @@ -41,7 +42,7 @@ Organization-owned spaces can be shared with other organization members, and you Alternatively, you can choose to grant "No access" to organization members, and keep the space hidden. -To share a organization-owned space with others: +To share an organization-owned space with others: 1. In the top right corner of the space, click **{% octicon "share" aria-hidden="true" aria-label="share" %}**. 1. To add specific users or teams, search for them with the search bar, then choose a role for the people you added. diff --git a/content/copilot/how-tos/provide-context/use-copilot-spaces/create-copilot-spaces.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/create-copilot-spaces.md similarity index 80% rename from content/copilot/how-tos/provide-context/use-copilot-spaces/create-copilot-spaces.md rename to content/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/create-copilot-spaces.md index 057e19285cea..a78119218fba 100644 --- a/content/copilot/how-tos/provide-context/use-copilot-spaces/create-copilot-spaces.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/create-copilot-spaces.md @@ -11,20 +11,19 @@ redirect_from: - /copilot/how-tos/context/copilot-spaces/creating-and-using-copilot-spaces - /copilot/how-tos/context/copilot-spaces/create-and-use-copilot-spaces - /copilot/how-tos/context/use-copilot-spaces/create-and-use-copilot-spaces + - /copilot/how-tos/provide-context/use-copilot-spaces/create-copilot-spaces contentType: how-tos category: - Author and optimize with Copilot --- -For an overview of {% data variables.copilot.copilot_spaces %}, see [AUTOTITLE](/copilot/concepts/about-organizing-and-sharing-context-with-copilot-spaces). - ## Creating a space 1. To create a space, go to [https://github.com/copilot/spaces](https://github.com/copilot/spaces?ref_product=copilot&ref_type=engagement&ref_style=text), and click **Create space**. 1. Give your space a name. 1. Choose whether the space is owned by you or by an organization you belong to. Organization-owned {% data variables.copilot.copilot_spaces_short %} can be shared using {% data variables.product.github %}’s built-in permission model. 1. Click **Create Space**. -1. Optionally, once you are in the space, under the space name, add a description. This does not affect the responses {% data variables.product.prodname_copilot_short %} gives in the space, but it can help others understand the context of the space. +1. Optionally, under the space name, add a description. The description does not affect {% data variables.product.prodname_copilot_short %}'s responses, but helps others understand the purpose of the space. >[!NOTE] You can change the name and description of your space at any time by hovering over them and clicking **{% octicon "pencil" aria-hidden="true" aria-label="pencil" %}**. @@ -51,9 +50,9 @@ You can add two types of context to your space: When adding sources to your space, you can choose to attach entire repositories or individual files. Understanding how each option works can help you get the best results from {% data variables.product.prodname_copilot_short %}. -* **Attach a repository**: When you attach a repository, {% data variables.product.prodname_copilot_short %} doesn't load the entire project into memory. Instead, it searches the repository and retrieves only the most relevant content needed to answer your question. This is recommended for large-scale use cases (for example, answering questions across all documentation in a repository). +* **Attach a repository**: When you attach a repository, {% data variables.product.prodname_copilot_short %} doesn't load the entire project into memory. Instead, it searches the repository and retrieves only the most relevant content for your question. This is best for large-scale use cases, such as answering questions across all documentation in a repository. -* **Attach individual files**: When you attach a file, its full contents are loaded into {% data variables.product.prodname_copilot_short %}'s context window and considered for every query in that space. This is best when you want {% data variables.product.prodname_copilot_short %} to consistently prioritize a specific document or small set of files. +* **Attach individual files**: When you attach a file, its full contents are loaded into {% data variables.product.prodname_copilot_short %}'s context window and considered for every query in that space. This is best when you want {% data variables.product.prodname_copilot_short %} to consistently prioritize a specific document or a small set of files. ## Adding context as you're working @@ -67,6 +66,7 @@ You can add files to a space directly from the code view on {% data variables.pr ## Next steps -* To learn more about using {% data variables.copilot.copilot_spaces_short %} in {% data variables.product.github %} and your IDE, see [AUTOTITLE](/copilot/how-tos/provide-context/use-copilot-spaces/use-copilot-spaces). -* To learn more about how to use {% data variables.copilot.copilot_spaces_short %} to help you with development work, see [AUTOTITLE](/copilot/using-github-copilot/copilot-spaces/speeding-up-development-work-with-copilot-spaces). -* To learn how to share your space with your team, see [AUTOTITLE](/copilot/using-github-copilot/copilot-spaces/collaborating-with-your-team-using-copilot-spaces). +* For an overview of {% data variables.copilot.copilot_spaces %}, see [AUTOTITLE](/copilot/concepts/about-organizing-and-sharing-context-with-copilot-spaces). +* To use {% data variables.copilot.copilot_spaces_short %} in {% data variables.product.github %} and your IDE, see [AUTOTITLE](/copilot/how-tos/provide-context/use-copilot-spaces/use-copilot-spaces). +* To speed up development work with {% data variables.copilot.copilot_spaces_short %}, see [AUTOTITLE](/copilot/using-github-copilot/copilot-spaces/speeding-up-development-work-with-copilot-spaces). +* To share your space with your team, see [AUTOTITLE](/copilot/using-github-copilot/copilot-spaces/collaborating-with-your-team-using-copilot-spaces). diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/index.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/index.md new file mode 100644 index 000000000000..3fe87c543f1a --- /dev/null +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/index.md @@ -0,0 +1,11 @@ +--- +title: '{% data variables.copilot.copilot_spaces %}' +shortTitle: '{% data variables.copilot.copilot_spaces_short %}' +intro: 'Organize and centralize relevant content into {% data variables.copilot.copilot_spaces_short %} that ground {% data variables.product.prodname_copilot_short %}''s responses in the right context for a specific task.' +versions: + feature: copilot +children: + - /create-copilot-spaces + - /collaborate-with-others +contentType: how-tos +--- diff --git a/content/copilot/how-tos/use-copilot-agents/cloud-agent/add-skills.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/add-skills.md similarity index 95% rename from content/copilot/how-tos/use-copilot-agents/cloud-agent/add-skills.md rename to content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/add-skills.md index 7417748a1112..38acf8769668 100644 --- a/content/copilot/how-tos/use-copilot-agents/cloud-agent/add-skills.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/add-skills.md @@ -12,6 +12,7 @@ category: redirect_from: - /copilot/how-tos/use-copilot-agents/coding-agent/create-skills - /copilot/how-tos/use-copilot-agents/cloud-agent/create-skills + - /copilot/how-tos/use-copilot-agents/cloud-agent/add-skills --- > [!NOTE] diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/create-custom-agents.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/create-custom-agents.md new file mode 100644 index 000000000000..11247c57b57f --- /dev/null +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/create-custom-agents.md @@ -0,0 +1,51 @@ +--- +title: Creating custom agents for {% data variables.copilot.copilot_cloud_agent %} +shortTitle: Create custom agents +intro: You can create specialized agents with tailored expertise for specific development tasks. +product: '{% data reusables.gated-features.copilot-cloud-agent %}
Sign up for {% data variables.product.prodname_copilot_short %} {% octicon "link-external" height:16 %}' +versions: + feature: copilot +category: + - Configure Copilot + - Author and optimize with Copilot +contentType: how-tos +redirect_from: + - /copilot/how-tos/use-copilot-agents/coding-agent/create-custom-agents + - /copilot/how-tos/use-copilot-agents/cloud-agent/create-custom-agents +--- + +{% data variables.copilot.custom_agents_caps_short %} allow you to tailor {% data variables.product.prodname_copilot_short %}'s expertise for specific tasks. For a conceptual overview of {% data variables.copilot.custom_agents_short %}, see [AUTOTITLE](/copilot/concepts/agents/cloud-agent/about-custom-agents). + +{% data reusables.copilot.custom-agents-ide-preview %} + +## Creating a {% data variables.copilot.copilot_custom_agent_short %} profile in a repository on {% data variables.product.github %} + +1. Go to the agents tab at [https://github.com/copilot/agents](https://github.com/copilot/agents?ref_product=copilot&ref_type=engagement&ref_style=text). + +1. Using the dropdown menu in the prompt box, select the repository you want to create the {% data variables.copilot.copilot_custom_agent_short %} profile in. + + > [!NOTE] + > Organization and enterprise owners can create organization and enterprise-level {% data variables.copilot.custom_agents_short %} in a `.github-private` repository that are available across all repositories within their organization or enterprise. For more information, see [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/prepare-for-custom-agents) and [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-organization/prepare-for-custom-agents). + +1. Optionally, select the branch you want to create the {% data variables.copilot.agent_profile %} in. The default is the main branch. +1. Click {% octicon "copilot" aria-label="Select a custom agent" %}, then click **{% octicon "plus" aria-label="Plus button" %} Create an agent**. This will open a template agent profile called `my-agent.agent.md` in the `.github/agents` directory of your target repository. +1. If you are creating an organization or enterprise-level {% data variables.copilot.copilot_custom_agent_short %}, delete the `.github/` portion of the file path to move your template to the root `agents` directory. +1. Edit the filename (the text before `.agent.md`), selecting a unique, descriptive name that identifies the agent's purpose. Note that the filename may only contain the following characters: `.`, `-`, `_`, `a-z`, `A-Z`, `0-9`. +1. Configure the {% data variables.copilot.agent_profile %}, including the name, description, tools, and prompts. For more information on what the {% data variables.copilot.agent_profile %} can include, see [Configuring an {% data variables.copilot.agent_profile %}](#configuring-an-agent-profile). +1. Commit the file to the repository and merge it into the default branch. Go back to the agents tab and refresh the page if needed. Your {% data variables.copilot.copilot_custom_agent_short %} will now appear in the dropdown when you click {% octicon "copilot" aria-hidden="true" aria-label="copilot" %} in the prompt box. + +## Configuring an {% data variables.copilot.agent_profile %} + +{% data reusables.copilot.custom-agents-configuring-profile %} + +## Example {% data variables.copilot.agent_profiles %} + +{% data reusables.copilot.custom-agents-example-profiles %} + +## Using {% data variables.copilot.custom_agents_short %} + +{% data reusables.copilot.custom-agents-using %} + +## Next steps + +{% data reusables.copilot.custom-agents-next-steps %} diff --git a/content/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-environment.md similarity index 95% rename from content/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment.md rename to content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-environment.md index 345cfa573aa7..17dab87bb29b 100644 --- a/content/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-environment.md @@ -1,8 +1,7 @@ --- -title: Customizing the development environment for GitHub Copilot cloud agent +title: Configure the development environment shortTitle: Customize the agent environment -allowTitleToDifferFromFilename: true -intro: 'Learn how to customize {% data variables.product.prodname_copilot %}''s development environment with additional tools.' +intro: 'Pre-install tools and dependencies so {% data variables.copilot.copilot_cloud_agent %} can build, test, and validate changes reliably.' versions: feature: copilot redirect_from: @@ -12,6 +11,7 @@ redirect_from: - /copilot/how-tos/agents/copilot-coding-agent/customizing-the-development-environment-for-copilot-coding-agent - /copilot/how-tos/agents/copilot-coding-agent/customize-the-agent-environment - /copilot/how-tos/agents/coding-agent/customize-the-agent-environment + - /copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment contentType: how-tos category: - Configure Copilot @@ -174,7 +174,7 @@ We recommend that you only use {% data variables.copilot.copilot_cloud_agent %} By default, {% data variables.product.prodname_copilot_short %} uses an Ubuntu Linux-based development environment. -You may want to use a Windows development environment if you're building software for Windows or your repository uses a Windows-based toolchain so {% data variables.product.prodname_copilot_short %} can build your project, run tests and validate its work. +You may want to use a Windows development environment if you're building software for Windows or your repository uses a Windows-based toolchain, so {% data variables.product.prodname_copilot_short %} can build your project, run tests, and validate its work. {% data variables.copilot.copilot_cloud_agent %}'s integrated firewall is not compatible with Windows, so we recommend that you only use self-hosted runners or larger {% data variables.product.prodname_dotcom %}-hosted runners with Azure private networking where you can implement your own network controls. For more information on runners with Azure private networking, see [AUTOTITLE](/admin/configuring-settings/configuring-private-networking-for-hosted-compute-products/about-azure-private-networking-for-github-hosted-runners-in-your-enterprise). @@ -204,7 +204,7 @@ jobs: You may want to set environment variables in {% data variables.product.prodname_copilot_short %}'s environment to configure or authenticate tools or dependencies that it has access to. -To set an environment variable for {% data variables.product.prodname_copilot_short %}, create a {% data variables.product.prodname_actions %} variable or secret in the `copilot` environment. If the value contains sensitive information, for example a password or API key, it's best to use a {% data variables.product.prodname_actions %} secret. +You may want to set an environment variable for {% data variables.product.prodname_copilot_short %}, create a {% data variables.product.prodname_actions %} variable or secret in the `copilot` environment. If the value contains sensitive information, for example a password or API key, it's best to use a {% data variables.product.prodname_actions %} secret. {% data reusables.repositories.navigate-to-repo %} {% data reusables.repositories.sidebar-settings %} diff --git a/content/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-firewall.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-firewall.md similarity index 90% rename from content/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-firewall.md rename to content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-firewall.md index daa3b75f5a09..f136807a242b 100644 --- a/content/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-firewall.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-firewall.md @@ -11,6 +11,7 @@ redirect_from: - /copilot/how-tos/agents/copilot-coding-agent/customizing-or-disabling-the-firewall-for-copilot-coding-agent - /copilot/how-tos/agents/copilot-coding-agent/customize-the-agent-firewall - /copilot/how-tos/agents/coding-agent/customize-the-agent-firewall + - /copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-firewall contentType: how-tos category: - Configure Copilot @@ -23,11 +24,11 @@ category: By default, {% data variables.product.prodname_copilot_short %}'s access to the internet is limited by a firewall. -Limiting access to the internet helps to manage data exfiltration risks, where surprising behavior from {% data variables.product.prodname_copilot_short %}, or malicious instructions given to it, could lead to code or other sensitive information being leaked to remote locations. +Limiting internet access helps manage data exfiltration risks. Unexpected behavior from {% data variables.product.prodname_copilot_short %}, or malicious instructions, could lead to code or other sensitive information being leaked to remote locations. The firewall always allows access to a number of hosts that {% data variables.product.prodname_copilot_short %} uses to interact with {% data variables.product.github %}. By default, a recommended allowlist is also enabled to allow the agent to download dependencies. -If {% data variables.product.prodname_copilot_short %} tries to make a request which is blocked by the firewall, a warning is added to the pull request body (if {% data variables.product.prodname_copilot_short %} is creating a pull request for the first time) or to a comment (if {% data variables.product.prodname_copilot_short %} is responding to a pull request comment). The warning shows the blocked address and the command that tried to make the request. +If {% data variables.product.prodname_copilot_short %} tries to make a request which is blocked by the firewall, a warning is added to the pull request body (for new pull requests) or to a comment (for existing pull requests). The warning shows the blocked address and the command that tried to make the request. ![Screenshot of a warning from {% data variables.product.prodname_copilot_short %} about being blocked by the firewall.](/assets/images/help/copilot/cloud-agent/firewall-warning.png) @@ -39,7 +40,7 @@ The agent firewall has important limitations that affect its security coverage. * **Only applies within the {% data variables.product.prodname_actions %} appliance**: The firewall only operates within the {% data variables.product.prodname_actions %} appliance environment. It does not apply to processes running outside of this environment. * **Bypass potential**: Sophisticated attacks may bypass the firewall, potentially allowing unauthorized network access and data exfiltration. -These limitations mean that the firewall provides a layer of protection for common scenarios, but should not be considered a comprehensive security solution. +These limitations mean that the firewall provides protection for common scenarios, but should not be considered a comprehensive security solution. ## Understanding the recommended firewall allowlist diff --git a/content/copilot/how-tos/use-copilot-agents/cloud-agent/extend-cloud-agent-with-mcp.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/extend-cloud-agent-with-mcp.md similarity index 97% rename from content/copilot/how-tos/use-copilot-agents/cloud-agent/extend-cloud-agent-with-mcp.md rename to content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/extend-cloud-agent-with-mcp.md index 74dd25e29fca..a8220aea2514 100644 --- a/content/copilot/how-tos/use-copilot-agents/cloud-agent/extend-cloud-agent-with-mcp.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/extend-cloud-agent-with-mcp.md @@ -1,8 +1,7 @@ --- -title: Extending GitHub Copilot cloud agent with the Model Context Protocol (MCP) +title: Connect agents to external tools shortTitle: Extend cloud agent with MCP -allowTitleToDifferFromFilename: true -intro: 'Learn how to use the Model Context Protocol (MCP) to extend the capabilities of {% data variables.copilot.copilot_cloud_agent %}.' +intro: 'Connect {% data variables.copilot.copilot_cloud_agent %} to external tools and data sources through the Model Context Protocol (MCP).' versions: feature: copilot redirect_from: @@ -14,6 +13,7 @@ redirect_from: - /copilot/how-tos/agents/copilot-coding-agent/extending-copilot-coding-agent-with-mcp - /copilot/how-tos/agents/copilot-coding-agent/extend-coding-agent-with-mcp - /copilot/how-tos/agents/coding-agent/extend-coding-agent-with-mcp + - /copilot/how-tos/use-copilot-agents/cloud-agent/extend-cloud-agent-with-mcp contentType: how-tos category: - Integrate Copilot with your tools @@ -92,7 +92,7 @@ The configuration object can contain the following keys: * A substitution reference to a secret or variable in your {% data variables.product.prodname_copilot_short %} environment, such as `$COPILOT_MCP_API_KEY` or `${COPILOT_MCP_API_KEY}`. Referenced names must start with `COPILOT_MCP_`. * A literal string value. -Note that all `string` and `string[]` fields besides `tools` & `type` support substitution with a variable or secret you have configured in your {% data variables.product.prodname_copilot_short %} environment. +Note that all `string` and `string[]` fields besides `tools` and `type` support substitution with a variable or secret you have configured in your {% data variables.product.prodname_copilot_short %} environment. ### Variable substitution @@ -107,7 +107,6 @@ The following syntax patterns are supported for referencing environment variable ## Example configurations The examples below show MCP server configurations for different providers. - * [Sentry](#example-sentry) * [Notion](#example-notion) * [Azure](#example-azure) diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/index.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/index.md new file mode 100644 index 000000000000..37b77c9e157b --- /dev/null +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/index.md @@ -0,0 +1,16 @@ +--- +title: 'Customize {% data variables.copilot.copilot_cloud_agent %}' +shortTitle: 'Customize {% data variables.copilot.copilot_cloud_agent_short %}' +intro: 'Create specialized agents with tailored expertise and extend their capabilities for {% data variables.copilot.copilot_cloud_agent %}.' +versions: + feature: copilot +children: + - /create-custom-agents + - /add-skills + - /extend-cloud-agent-with-mcp + - /use-hooks + - /customize-the-agent-environment + - /customize-the-agent-firewall + - /test-custom-agents +contentType: how-tos +--- diff --git a/content/copilot/how-tos/use-copilot-agents/cloud-agent/test-custom-agents.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/test-custom-agents.md similarity index 94% rename from content/copilot/how-tos/use-copilot-agents/cloud-agent/test-custom-agents.md rename to content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/test-custom-agents.md index e5b892897fb9..95cc1cdb18fe 100644 --- a/content/copilot/how-tos/use-copilot-agents/cloud-agent/test-custom-agents.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/test-custom-agents.md @@ -10,6 +10,7 @@ category: - Manage Copilot for a team redirect_from: - /copilot/how-tos/use-copilot-agents/coding-agent/test-custom-agents + - /copilot/how-tos/use-copilot-agents/cloud-agent/test-custom-agents --- {% data reusables.copilot.custom-agents-preview-note %} @@ -30,7 +31,7 @@ Before you can test a {% data variables.copilot.copilot_custom_agent_short %}, y ## 2. Test your {% data variables.copilot.copilot_custom_agent_short %} -1. Navigate to the agents tab at [https://github.com/copilot/agents](https://github.com/copilot/agents?ref_product=copilot&ref_type=engagement&ref_style=text). +1. Go to the agents tab at [https://github.com/copilot/agents](https://github.com/copilot/agents?ref_product=copilot&ref_type=engagement&ref_style=text). 1. Using the dropdown menu in the prompt box, select your `.github-private` repository. 1. Select {% octicon "copilot" aria-label="Select a custom agent" %}, then click your test agent. 1. To test your {% data variables.copilot.copilot_custom_agent_short %}, send {% data variables.product.prodname_copilot_short %} a prompt. @@ -46,4 +47,4 @@ Before you can test a {% data variables.copilot.copilot_custom_agent_short %}, y To monitor the usage of {% data variables.copilot.custom_agents_short %} in your organization, filter your organization's audit log by `actor:Copilot`. See [AUTOTITLE](/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/reviewing-the-audit-log-for-your-organization). -To monitor the usage of {% data variables.copilot.custom_agents_short %} in your enterprise, see [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/monitor-agentic-activity). +To monitor the usage of {% data variables.copilot.custom_agents_short %} in your enterprise, see [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/monitor-agentic-activity). \ No newline at end of file diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/use-hooks.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/use-hooks.md new file mode 100644 index 000000000000..bbbf6e24174a --- /dev/null +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/use-hooks.md @@ -0,0 +1,30 @@ +--- +title: Customize agent workflows with hooks +shortTitle: Use hooks +intro: 'Run automated checks—like linting, formatting, or security scans—at key points during agent execution to enforce quality standards.' +versions: + feature: copilot +contentType: how-tos +category: + - Configure Copilot +redirect_from: + - /copilot/how-tos/use-copilot-agents/coding-agent/use-hooks + - /copilot/how-tos/use-copilot-agents/cloud-agent/use-hooks +--- + +{% data reusables.copilot.cloud-agent.hooks-intro %} + +## Creating a hook in a repository on {% data variables.product.github %} + +{% data reusables.copilot.cloud-agent.create-hooks-instructions %} + +## Troubleshooting + +{% data reusables.copilot.cloud-agent.troubleshoot-hooks %} + +## Further reading + +* [AUTOTITLE](/copilot/reference/hooks-configuration) +* [AUTOTITLE](/copilot/concepts/agents/cloud-agent/about-cloud-agent) +* [AUTOTITLE](/copilot/concepts/agents/about-copilot-cli) +* [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment) diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-copilot-overview.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-copilot-overview.md new file mode 100644 index 000000000000..94e0c6ff4317 --- /dev/null +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-copilot-overview.md @@ -0,0 +1,94 @@ +--- +title: 'Customize {% data variables.product.prodname_copilot_short %} for your project' +shortTitle: Customize {% data variables.product.prodname_copilot_short %} overview +intro: 'Set up custom instructions, create a specialized agent, and organize project context on {% data variables.product.github %}.' +versions: + feature: copilot +contentType: how-tos +allowTitleToDifferFromFilename: true +--- + +This quickstart walks you through customizing {% data variables.product.prodname_copilot %} for a repository. By the end, {% data variables.product.prodname_copilot_short %} will know your project's conventions, have a specialized agent for common tasks, and have curated project context. + +**Scenario:** You work on a team's web application repository with an established test suite, coding conventions, and active issues. You want {% data variables.product.prodname_copilot_short %} to work effectively with the codebase from day one. + +## Prerequisites + +* A {% data variables.product.prodname_copilot_short %} plan that includes {% data variables.copilot.copilot_cloud_agent %} ({% data variables.copilot.copilot_pro_plus %}, {% data variables.copilot.copilot_business_short %}, or {% data variables.copilot.copilot_enterprise_short %}). For more information, see [AUTOTITLE](/copilot/about-github-copilot/subscription-plans-for-github-copilot). +* {% data variables.copilot.copilot_cloud_agent_short_cap_c %} enabled for your organization or account. For more information, see [AUTOTITLE](/copilot/concepts/agents/cloud-agent/access-management). +* Write access to a {% data variables.product.github %} repository. + +## Step 1: Teach {% data variables.product.prodname_copilot_short %} your project's conventions + +Repository custom instructions give {% data variables.product.prodname_copilot_short %} persistent context about your project—its structure, coding standards, and how to build and test code. Every {% data variables.product.prodname_copilot_short %} interaction in the repository uses these instructions automatically. + +Ask {% data variables.copilot.copilot_cloud_agent %} to generate a `copilot-instructions.md` file: + +1. Go to [github.com/copilot/agents](https://github.com/copilot/agents?ref_product=copilot&ref_type=engagement&ref_style=text). +1. Select your repository from the dropdown menu in the prompt field. +1. Enter the following prompt: + + ```text copy + Onboard this repository to Copilot cloud agent by adding a + .github/copilot-instructions.md file. Include information about project + structure, coding conventions, the test framework, and how to build and + run the project. + ``` + +1. Review the generated file and merge the pull request. + +{% data variables.product.prodname_copilot_short %} now understands your project's conventions across chat, code review, and agent sessions. See [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions). + +## Step 2: Create a specialized agent + +{% data variables.copilot.custom_agents_caps_short %} let you create focused assistants for recurring tasks. In this example, create an agent that diagnoses and fixes bugs. + +1. Go to [github.com/copilot/agents](https://github.com/copilot/agents?ref_product=copilot&ref_type=engagement&ref_style=text) and select your repository. +1. In the prompt field, click {% octicon "copilot" aria-label="Select a custom agent" %}. Then click **{% octicon "plus" aria-label="Plus button" %} Create a custom agent**. +1. Rename the file to `bug-fixer.agent.md`. +1. Replace the template content with: + + ```yaml copy + --- + name: Bug Fixer + description: Diagnoses and fixes bugs reported in GitHub issues. + tools: + - read + - edit + - terminal + - search + --- + + You are a bug-fixing specialist. When given a bug report or issue: + + 1. Reproduce the bug by writing a failing test. + 2. Identify the root cause. + 3. Fix the code. + 4. Verify the fix passes the test and doesn't break existing tests. + + Always follow the project's testing conventions and coding standards. + ``` + +1. Commit the file and merge it into the default branch. + +Your bug-fixer agent now appears in the agents dropdown on the agents tab. Select it before pasting an issue URL to start a focused debugging session. See [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/create-custom-agents). + +## Step 3: Organize project context with a space + +{% data variables.copilot.copilot_spaces %} let you curate the exact context {% data variables.product.prodname_copilot_short %} needs for a specific area of your project. Answers are grounded in relevant files, issues, and documentation. + +1. Go to [github.com/copilot/spaces](https://github.com/copilot/spaces?ref_product=copilot&ref_type=engagement&ref_style=text) and click **Create space**. +1. Name the space (for example, "API Architecture") and choose an owner. +1. Click **{% octicon "plus" aria-hidden="true" aria-label="plus" %} Add sources**, then add context that's relevant to your project: + * **{% octicon "file-code" aria-hidden="true" aria-label="file-code" %} Add files and repositories** — Add architecture docs, API schemas, or key configuration files. + * **{% octicon "link" aria-hidden="true" aria-label="link" %} Link files, pull requests, and issues** — Paste URLs for active issues or design discussions. +1. In the space's chat, ask a question like: "What patterns does our API use for error handling?" + +{% data variables.product.prodname_copilot_short %} answers using only the context you've curated. See [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/create-copilot-spaces). + +## Next steps + +* **[AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-personal-instructions)** — Set personal preferences that apply across all your repositories. +* **[AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/add-skills)** — Add new capabilities to your agents. +* **[AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/extend-cloud-agent-with-mcp)** — Connect agents to external tools and services. +* **[AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/copilot-spaces/collaborate-with-others)** — Share your spaces with teammates. diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/index.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/index.md new file mode 100644 index 000000000000..2c37f334c0cd --- /dev/null +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/index.md @@ -0,0 +1,13 @@ +--- +title: 'Customize {% data variables.product.prodname_copilot_short %}' +shortTitle: Customize {% data variables.product.prodname_copilot_short %} +intro: 'Customize {% data variables.product.prodname_copilot %} to fit your needs and get better results.' +versions: + feature: copilot +children: + - /customize-copilot-overview + - /add-custom-instructions + - /customize-cloud-agent + - /copilot-spaces +contentType: how-tos +--- diff --git a/content/copilot/how-tos/copilot-on-github/index.md b/content/copilot/how-tos/copilot-on-github/index.md index 3079db935f99..baff5c9ec73e 100644 --- a/content/copilot/how-tos/copilot-on-github/index.md +++ b/content/copilot/how-tos/copilot-on-github/index.md @@ -7,6 +7,7 @@ versions: children: - /set-up-copilot - /chat-with-copilot + - /customize-copilot - /copilot-for-github-tasks - /use-copilot-agents contentType: how-tos diff --git a/content/copilot/how-tos/index.md b/content/copilot/how-tos/index.md index 394268b11e79..c821202df6cc 100644 --- a/content/copilot/how-tos/index.md +++ b/content/copilot/how-tos/index.md @@ -14,7 +14,7 @@ children: - /use-copilot-agents - /use-ai-models - /provide-context - - /configure-custom-instructions + - /configure-custom-instructions-in-your-ide - /configure-content-exclusion - /use-copilot-for-common-tasks - /configure-personal-settings diff --git a/content/copilot/how-tos/provide-context/use-copilot-spaces/index.md b/content/copilot/how-tos/provide-context/use-copilot-spaces/index.md index 80315fc609d3..0728c87e4323 100644 --- a/content/copilot/how-tos/provide-context/use-copilot-spaces/index.md +++ b/content/copilot/how-tos/provide-context/use-copilot-spaces/index.md @@ -5,9 +5,7 @@ intro: "Organize and centralize relevant content into {% data variables.copilot. versions: feature: copilot children: - - /create-copilot-spaces - /use-copilot-spaces - - /collaborate-with-others redirect_from: - /copilot/using-github-copilot/copilot-spaces - /copilot/how-tos/context/copilot-spaces diff --git a/content/copilot/how-tos/provide-context/use-copilot-spaces/use-copilot-spaces.md b/content/copilot/how-tos/provide-context/use-copilot-spaces/use-copilot-spaces.md index c314e78dfb9d..676de7eebbc7 100644 --- a/content/copilot/how-tos/provide-context/use-copilot-spaces/use-copilot-spaces.md +++ b/content/copilot/how-tos/provide-context/use-copilot-spaces/use-copilot-spaces.md @@ -12,14 +12,6 @@ category: For information on creating {% data variables.copilot.copilot_spaces %}, see [AUTOTITLE](/copilot/how-tos/provide-context/use-copilot-spaces/create-copilot-spaces). -## Using {% data variables.copilot.copilot_spaces %} in {% data variables.product.github %} - -Once you've added context to your space, you can ask {% data variables.product.prodname_copilot_short %} questions in the space's chat interface in {% data variables.product.github %}. Your chat will be grounded in the context you've added. You can view all conversations you have had in the space's "Conversations" tab. - -You can also change the large language model (LLM) used for your space by selecting the **CURRENT-MODEL** {% octicon "chevron-down" aria-hidden="true" aria-label="chevron-down" %} dropdown menu, then clicking the AI model of your choice. For more information, see [AUTOTITLE](/copilot/reference/ai-models/model-comparison). - -To star your favorite spaces so that you can easily find them later, you can click **{% octicon "star" aria-hidden="true" aria-label="star" %}** in the top right corner of the space. To view all spaces available to you, including starred spaces, go to [https://github.com/copilot/spaces](https://github.com/copilot/spaces?ref_product=copilot&ref_type=engagement&ref_style=text). - ## Using {% data variables.copilot.copilot_spaces %} in your IDE You can also access the information and context from {% data variables.copilot.copilot_spaces_short %} directly in your IDE using the {% data variables.product.github %} MCP server. This allows you to leverage your curated context while coding without switching between your IDE and the web interface. diff --git a/content/copilot/how-tos/use-copilot-agents/cloud-agent/create-custom-agents.md b/content/copilot/how-tos/use-copilot-agents/cloud-agent/create-custom-agents-in-your-ide.md similarity index 56% rename from content/copilot/how-tos/use-copilot-agents/cloud-agent/create-custom-agents.md rename to content/copilot/how-tos/use-copilot-agents/cloud-agent/create-custom-agents-in-your-ide.md index ced4a9130f51..3414cc2224fa 100644 --- a/content/copilot/how-tos/use-copilot-agents/cloud-agent/create-custom-agents.md +++ b/content/copilot/how-tos/use-copilot-agents/cloud-agent/create-custom-agents-in-your-ide.md @@ -1,6 +1,6 @@ --- -title: Creating custom agents for {% data variables.copilot.copilot_cloud_agent %} -shortTitle: Create custom agents +title: Creating custom agents for {% data variables.copilot.copilot_cloud_agent %} in your IDE +shortTitle: Create custom agents in your IDE intro: You can create specialized agents with tailored expertise for specific development tasks. product: '{% data reusables.gated-features.copilot-cloud-agent %}
Sign up for {% data variables.product.prodname_copilot_short %} {% octicon "link-external" height:16 %}' versions: @@ -9,30 +9,12 @@ category: - Configure Copilot - Author and optimize with Copilot contentType: how-tos -redirect_from: - - /copilot/how-tos/use-copilot-agents/coding-agent/create-custom-agents --- {% data variables.copilot.custom_agents_caps_short %} allow you to tailor {% data variables.product.prodname_copilot_short %}'s expertise for specific tasks. For a conceptual overview of {% data variables.copilot.custom_agents_short %}, see [AUTOTITLE](/copilot/concepts/agents/cloud-agent/about-custom-agents). {% data reusables.copilot.custom-agents-ide-preview %} -## Creating a {% data variables.copilot.copilot_custom_agent_short %} profile in a repository on {% data variables.product.github %} - -1. Navigate to the agents tab at [https://github.com/copilot/agents](https://github.com/copilot/agents?ref_product=copilot&ref_type=engagement&ref_style=text). - -1. Using the dropdown menu in the prompt box, select the repository you want to create the {% data variables.copilot.copilot_custom_agent_short %} profile in. - - > [!NOTE] - > Organization and enterprise owners can create organization and enterprise-level {% data variables.copilot.custom_agents_short %} in a `.github-private` repository that are available across all repositories within their organization or enterprise. For more information, see [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-enterprise/manage-agents/prepare-for-custom-agents) and [AUTOTITLE](/copilot/how-tos/administer-copilot/manage-for-organization/prepare-for-custom-agents). - -1. Optionally, select the branch you want to create the {% data variables.copilot.agent_profile %} in. The default is the main branch. -1. Click {% octicon "copilot" aria-label="Select a custom agent" %}, then click **{% octicon "plus" aria-label="Plus button" %} Create an agent**. This will open a template agent profile called `my-agent.agent.md` in the `.github/agents` directory of your target repository. -1. If you are creating an organization or enterprise-level {% data variables.copilot.copilot_custom_agent_short %}, delete the `.github/` portion of the file path to move your template to the root `agents` directory. -1. Edit the filename (the text before `.agent.md`), selecting a unique, descriptive name that identifies the agent's purpose. Note that the filename may only contain the following characters: `.`, `-`, `_`, `a-z`, `A-Z`, `0-9`. -1. Configure the {% data variables.copilot.agent_profile %}, including the name, description, tools, and prompts. For more information on what the {% data variables.copilot.agent_profile %} can include, see [Configuring an {% data variables.copilot.agent_profile %}](#configuring-an-agent-profile). -1. Commit the file to the repository and merge it into the default branch. Go back to the agents tab and refresh the page if needed. Your {% data variables.copilot.copilot_custom_agent_short %} will now appear in the dropdown when you click {% octicon "copilot" aria-hidden="true" aria-label="copilot" %} in the prompt box. - ## Creating a {% data variables.copilot.copilot_custom_agent_short %} profile in {% data variables.product.prodname_vscode %} 1. Open {% data variables.copilot.copilot_chat %} in {% data variables.product.prodname_vscode %}. @@ -81,19 +63,7 @@ To update an {% data variables.copilot.agent_profile %}, from the agents dropdow ## Configuring an {% data variables.copilot.agent_profile %} -An {% data variables.copilot.agent_profile %} is a Markdown file with YAML frontmatter that specifies the {% data variables.copilot.copilot_custom_agent_short %}'s name, description, available tools, and MCP server configurations. Configuring an {% data variables.copilot.agent_profile %} involves defining the agent's identity, capabilities, tool access, and behavioral instructions. - -For detailed configuration information about YAML properties, tools, MCP server setup, tool aliases, and how {% data variables.copilot.custom_agents_short %} are processed, see [AUTOTITLE](/copilot/reference/custom-agents-configuration). - -To configure your {% data variables.copilot.agent_profile %}: - -1. Optionally, write a `name` for your {% data variables.copilot.copilot_custom_agent_short %}. If unset, the name will default to the filename (without the `.md` or `.agent.md` suffix). -1. Write a brief `description` (required) explaining what your agent does and its specific capabilities or domain expertise. -1. In the `tools` property, define which tools the agent can use. This is a list of tool names or aliases, including tools from MCP servers configured in the repository settings or the {% data variables.copilot.agent_profile %} (for example, `tools: ["read", "edit", "search", "some-mcp-server/tool-1"]`). If you omit this property, the agent will have access to all available tools. See "Tools" in [AUTOTITLE](/copilot/reference/custom-agents-configuration#tools). -1. Optionally, in the `mcp-servers` property, you can configure MCP servers that will be available only to this agent to extend its capabilities. See "MCP server configuration details" in [AUTOTITLE](/copilot/reference/custom-agents-configuration#mcp-server-configuration-details). -1. If you are creating and using the {% data variables.copilot.agent_profile %} in {% data variables.product.prodname_vscode_shortname %}, JetBrains IDEs, Eclipse, or Xcode, you can also use the `model` property to control which AI model the agent should use. -1. Optionally, set the `target` property to either `vscode` or `github-copilot` if you want to only use the agent in a specific environment. The agent will be available in both environments if you omit the property. -1. Write the agent's prompt. Define the agent's behavior, expertise, and instructions in the Markdown content below the YAML frontmatter. The prompt can be a maximum of 30,000 characters. +{% data reusables.copilot.custom-agents-configuring-profile %} ## Example {% data variables.copilot.agent_profiles %} @@ -101,15 +71,7 @@ To configure your {% data variables.copilot.agent_profile %}: ## Using {% data variables.copilot.custom_agents_short %} -Once you've created a {% data variables.copilot.copilot_custom_agent_short %}, you can use it wherever {% data variables.copilot.copilot_cloud_agent %} is available. - -* When prompting {% data variables.copilot.copilot_cloud_agent %} with a task on {% data variables.product.prodname_dotcom_the_website %}, use the dropdown menu in the agents panel or agents tab to select your {% data variables.copilot.copilot_custom_agent_short %} instead of the default {% data variables.copilot.copilot_cloud_agent_short %}. -* When assigning {% data variables.copilot.copilot_cloud_agent %} to an issue, you can select your {% data variables.copilot.copilot_custom_agent_short %} from the dropdown menu to handle the issue with your specialized configuration. -* When using the {% data variables.copilot.copilot_cli %}, you can choose to use a particular {% data variables.copilot.copilot_custom_agent_short %} by using the `/agent` slash command or referencing the agent in a prompt or via a command-line argument. For more information, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/use-copilot-cli#use-custom-agents). - -When {% data variables.product.prodname_copilot_short %} opens pull requests, it will note which {% data variables.copilot.copilot_custom_agent_short %} was used to complete the work in the pull request description. - -For more information on using {% data variables.copilot.copilot_cloud_agent %}, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/create-a-pr). +{% data reusables.copilot.custom-agents-using %} ### Using {% data variables.copilot.custom_agents_short %} in your IDE @@ -121,6 +83,4 @@ For more information on {% data variables.copilot.custom_agents_short %} in {% d ## Next steps -* For a hands-on tutorial to create your first {% data variables.copilot.copilot_custom_agent_short %}, see [AUTOTITLE](/copilot/tutorials/customization-library/custom-agents/your-first-custom-agent). -* For detailed configuration information, see [AUTOTITLE](/copilot/reference/custom-agents-configuration). -* For information on using cloud agents, including your {% data variables.copilot.custom_agents_short %}, to create pull requests, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/create-a-pr). +{% data reusables.copilot.custom-agents-next-steps %} diff --git a/content/copilot/how-tos/use-copilot-agents/cloud-agent/index.md b/content/copilot/how-tos/use-copilot-agents/cloud-agent/index.md index ccc6aeadc5c2..02ba41d9de7b 100644 --- a/content/copilot/how-tos/use-copilot-agents/cloud-agent/index.md +++ b/content/copilot/how-tos/use-copilot-agents/cloud-agent/index.md @@ -8,10 +8,6 @@ versions: children: - /create-a-pr - /track-copilot-sessions - - /create-custom-agents - - /test-custom-agents - - /add-skills - - /extend-cloud-agent-with-mcp - /integrate-cloud-agent-with-jira - /integrate-cloud-agent-with-slack - /integrate-cloud-agent-with-teams @@ -19,9 +15,7 @@ children: - /integrate-cloud-agent-with-azure-boards - /changing-the-ai-model - /configuring-agent-settings - - /customize-the-agent-environment - - /customize-the-agent-firewall - - /use-hooks + - /create-custom-agents-in-your-ide - /troubleshoot-cloud-agent redirect_from: - /copilot/how-tos/use-copilot-agents/coding-agent diff --git a/content/copilot/how-tos/use-copilot-agents/cloud-agent/use-hooks.md b/content/copilot/how-tos/use-copilot-agents/cloud-agent/use-hooks.md deleted file mode 100644 index aa8e897d9c6b..000000000000 --- a/content/copilot/how-tos/use-copilot-agents/cloud-agent/use-hooks.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Using hooks with GitHub Copilot agents -shortTitle: Use hooks -intro: 'Extend and customize {% data variables.product.prodname_copilot %} agent behavior by executing custom shell commands at key points during agent execution.' -versions: - feature: copilot -contentType: how-tos -category: - - Configure Copilot -redirect_from: - - /copilot/how-tos/use-copilot-agents/coding-agent/use-hooks ---- - -{% data reusables.copilot.cloud-agent.hooks-intro %} - -## Creating a hook in a repository on {% data variables.product.github %} - -{% data reusables.copilot.cloud-agent.create-hooks-instructions %} - -## Troubleshooting - -{% data reusables.copilot.cloud-agent.troubleshoot-hooks %} - -## Further reading - -* For more information about configuring hooks, see [AUTOTITLE](/copilot/reference/hooks-configuration) -* For more information about {% data variables.copilot.copilot_cloud_agent %}, see [AUTOTITLE](/copilot/concepts/agents/cloud-agent/about-cloud-agent) -* For more information about {% data variables.copilot.copilot_cli %}, see [AUTOTITLE](/copilot/concepts/agents/about-copilot-cli) -* For information about customizing the agent environment, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment) diff --git a/content/copilot/reference/copilot-cli-reference/cli-command-reference.md b/content/copilot/reference/copilot-cli-reference/cli-command-reference.md index e5d7181108e3..4a1dd20b8dac 100644 --- a/content/copilot/reference/copilot-cli-reference/cli-command-reference.md +++ b/content/copilot/reference/copilot-cli-reference/cli-command-reference.md @@ -138,7 +138,7 @@ COPILOT_GITHUB_TOKEN=github_pat_... copilot | `/model`, `/models [MODEL]` | Select the AI model you want to use. | | `/plan [PROMPT]` | Create an implementation plan before coding. | | `/plugin [marketplace\|install\|uninstall\|update\|list] [ARGS...]` | Manage plugins and plugin marketplaces. See [AUTOTITLE](/copilot/concepts/agents/copilot-cli/about-cli-plugins). | -| `/pr [view\|create\|fix\|auto]` | Operate on pull requests for the current branch. | +| `/pr [view\|create\|fix\|auto]` | Manage pull requests for the current branch. See [AUTOTITLE](/copilot/how-tos/copilot-cli/manage-pull-requests). | | `/remote` | Enable remote access to this session from {% data variables.product.prodname_dotcom_the_website %} and {% data variables.product.prodname_mobile %}. See [AUTOTITLE](/copilot/how-tos/copilot-cli/steer-remotely). | | `/rename [NAME]` | Rename the current session (auto-generates a name if omitted; alias for `/session rename`). | | `/research TOPIC` | Run a deep research investigation using {% data variables.product.github %} search and web sources. See [AUTOTITLE](/copilot/concepts/agents/copilot-cli/research). | diff --git a/data/reusables/copilot/custom-agents-configuring-profile.md b/data/reusables/copilot/custom-agents-configuring-profile.md new file mode 100644 index 000000000000..0149e1df0e16 --- /dev/null +++ b/data/reusables/copilot/custom-agents-configuring-profile.md @@ -0,0 +1,13 @@ +An {% data variables.copilot.agent_profile %} is a Markdown file with YAML frontmatter that specifies the {% data variables.copilot.copilot_custom_agent_short %}'s name, description, available tools, and MCP server configurations. Configuring an {% data variables.copilot.agent_profile %} involves defining the agent's identity, capabilities, tool access, and behavioral instructions. + +For detailed configuration information about YAML properties, tools, MCP server setup, tool aliases, and how {% data variables.copilot.custom_agents_short %} are processed, see [AUTOTITLE](/copilot/reference/custom-agents-configuration). + +To configure your {% data variables.copilot.agent_profile %}: + +1. Optionally, write a `name` for your {% data variables.copilot.copilot_custom_agent_short %}. If unset, the name will default to the filename (without the `.md` or `.agent.md` suffix). +1. Write a brief `description` (required) explaining what your agent does and its specific capabilities or domain expertise. +1. In the `tools` property, define which tools the agent can use. This is a list of tool names or aliases, including tools from MCP servers configured in the repository settings or the {% data variables.copilot.agent_profile %} (for example, `tools: ["read", "edit", "search", "some-mcp-server/tool-1"]`). If you omit this property, the agent will have access to all available tools. See "Tools" in [AUTOTITLE](/copilot/reference/custom-agents-configuration#tools). +1. Optionally, in the `mcp-servers` property, you can configure MCP servers that will be available only to this agent to extend its capabilities. See "MCP server configuration details" in [AUTOTITLE](/copilot/reference/custom-agents-configuration#mcp-server-configuration-details). +1. If you are creating and using the {% data variables.copilot.agent_profile %} in {% data variables.product.prodname_vscode_shortname %}, JetBrains IDEs, Eclipse, or Xcode, you can also use the `model` property to control which AI model the agent should use. +1. Optionally, set the `target` property to either `vscode` or `github-copilot` if you want to only use the agent in a specific environment. The agent will be available in both environments if you omit the property. +1. Write the agent's prompt. Define the agent's behavior, expertise, and instructions in the Markdown content below the YAML frontmatter. The prompt can be a maximum of 30,000 characters. diff --git a/data/reusables/copilot/custom-agents-next-steps.md b/data/reusables/copilot/custom-agents-next-steps.md new file mode 100644 index 000000000000..94a40853352c --- /dev/null +++ b/data/reusables/copilot/custom-agents-next-steps.md @@ -0,0 +1,3 @@ +* For a hands-on tutorial to create your first {% data variables.copilot.copilot_custom_agent_short %}, see [AUTOTITLE](/copilot/tutorials/customization-library/custom-agents/your-first-custom-agent). +* For detailed configuration information, see [AUTOTITLE](/copilot/reference/custom-agents-configuration). +* For information on using cloud agents, including your {% data variables.copilot.custom_agents_short %}, to create pull requests, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/create-a-pr). diff --git a/data/reusables/copilot/custom-agents-using.md b/data/reusables/copilot/custom-agents-using.md new file mode 100644 index 000000000000..bb5194b0f6fd --- /dev/null +++ b/data/reusables/copilot/custom-agents-using.md @@ -0,0 +1,9 @@ +Once you've created a {% data variables.copilot.copilot_custom_agent_short %}, you can use it wherever {% data variables.copilot.copilot_cloud_agent %} is available. + +* When prompting {% data variables.copilot.copilot_cloud_agent %} with a task on {% data variables.product.prodname_dotcom_the_website %}, use the dropdown menu in the agents panel or agents tab to select your {% data variables.copilot.copilot_custom_agent_short %} instead of the default {% data variables.copilot.copilot_cloud_agent_short %}. +* When assigning {% data variables.copilot.copilot_cloud_agent %} to an issue, you can select your {% data variables.copilot.copilot_custom_agent_short %} from the dropdown menu to handle the issue with your specialized configuration. +* When using the {% data variables.copilot.copilot_cli %}, you can choose to use a particular {% data variables.copilot.copilot_custom_agent_short %} by using the `/agent` slash command or referencing the agent in a prompt or via a command-line argument. For more information, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/use-copilot-cli#use-custom-agents). + +When {% data variables.product.prodname_copilot_short %} opens pull requests, it will note which {% data variables.copilot.copilot_custom_agent_short %} was used to complete the work in the pull request description. + +For more information on using {% data variables.copilot.copilot_cloud_agent %}, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/create-a-pr). diff --git a/src/languages/lib/correct-translation-content.ts b/src/languages/lib/correct-translation-content.ts index 2e1eb3fed434..7730a196b5b4 100644 --- a/src/languages/lib/correct-translation-content.ts +++ b/src/languages/lib/correct-translation-content.ts @@ -20,6 +20,53 @@ export function correctTranslatedContentStrings( englishContent: string, context: CorrectionContext = {}, ): string { + // --- Universal pre-fixes (run before per-language rules) --- + + // Translators sometimes inserted spaces inside Liquid delimiters, + // breaking the tags (e.g. `{ % endif %}`, `{% endif % }`). Collapse + // these — but only when the tag has actual non-whitespace content + // inside, so we don't disturb the special `{% }` → `{% endif %}` + // recovery handled later. The English source never contains these + // patterns, so this is safe globally. + content = content.replace(/\{\s*%(-?)(\s*\S[^%]*?\s*)(-?)%\s*\}/g, '{%$1$2$3%}') + + // Translators sometimes dropped the `data` keyword in front of a + // `variables.X.Y` / `reusables.X.Y` / `product.X` path. The English + // source never starts a Liquid tag with these prefixes; they always + // come inside `{% data variables.X %}` or similar. Restore the keyword. + content = content.replace( + /\{%(-?)\s+(variables|reusables)\.([A-Za-z0-9._-]+)(\s*-?%\})/g, + '{%$1 data $2.$3$4', + ) + // `{% product.prodname_X %}` → `{% data variables.product.prodname_X %}` + content = content.replace( + /\{%(-?)\s+(product\.[A-Za-z0-9._-]+)(\s*-?%\})/g, + '{%$1 data variables.$2$3', + ) + + // Translators sometimes wrote `{% data.variables.X %}` (period instead + // of space) or `{% data.reusables.X %}` / `{% data.product.X %}`, + // which Liquid parses as a single variable lookup whose name starts + // with `.variables` / `.reusables`. Restore the space. + content = content.replace( + /\{%(-?)\s*data\.(variables|reusables)\.([A-Za-z0-9._-]+)(\s*-?%\})/g, + '{%$1 data $2.$3$4', + ) + content = content.replace( + /\{%(-?)\s*data\.(product\.[A-Za-z0-9._-]+)(\s*-?%\})/g, + '{%$1 data variables.$2$3', + ) + + // The translation pipeline frequently splits Markdown bullet markers + // (`*`) and table-cell pipes (`|`) onto their own line, with the + // actual content pushed to the next line as deeply indented text. + // This breaks list and table rendering and leaves `[AUTOTITLE]` links + // unexpanded. Rejoin the marker with its content. This corruption + // affects every translated language (~47k bullets and ~11k cells in + // total), so it lives in the universal pre-fixes block. + content = content.replace(/^\* ?\n[ \t]+/gm, '* ') + content = content.replace(/^\|[ \t]*\n[ \t]+/gm, '| ') + // --- Per-language fixes (es, ja, pt, zh, ru, fr, ko, de) --- if (context.code === 'es') { @@ -102,6 +149,20 @@ export function correctTranslatedContentStrings( // `{% encabezados de fila %}` — "row headers" = rowheaders content = content.replaceAll('{% encabezados de fila %}', '{% rowheaders %}') content = content.replaceAll('{%- encabezados de fila %}', '{%- rowheaders %}') + // Spanish `o` = "or", `y` = "and" inside ifversion/elsif/if + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?\so\s[^%]*?-?%\}/g, (m) => + m.replace(/\so\s/g, ' or '), + ) + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?\sy\s[^%]*?-?%\}/g, (m) => + m.replace(/\sy\s/g, ' and '), + ) + // `{% ifversion ghes}` (missing `%` before `}`) — translator dropped the + // closing percent. Match plan name (fpt|ghec|ghes|ghae) followed by `}` + // not `%}`, immediately followed by content (so we don't over-match). + content = content.replace( + /\{%-?(\s+(?:ifversion|elsif|if)\s+(?:not\s+)?(?:fpt|ghec|ghes|ghae)(?:\s+(?:or|and)\s+(?:not\s+)?(?:fpt|ghec|ghes|ghae))*)\}/g, + '{%$1 %}', + ) } if (context.code === 'ja') { @@ -236,6 +297,74 @@ export function correctTranslatedContentStrings( // `{% ウィンドウ %}` — "Window" (without ズ suffix) = windows (alternate transliteration) content = content.replaceAll('{% ウィンドウ %}', '{% windows %}') content = content.replaceAll('{%- ウィンドウ %}', '{%- windows %}') + // `{% デスクトップ %}` — "desktop" (Japanese transliteration) = desktop platform tag + content = content.replaceAll('{% デスクトップ %}', '{% desktop %}') + content = content.replaceAll('{%- デスクトップ %}', '{%- desktop %}') + // `{%データ` (no space after `{%`) — also catches `{%データvariables` + content = content.replaceAll('{%データvariables', '{% data variables') + content = content.replaceAll('{%データ variables', '{% data variables') + content = content.replaceAll('{%- データvariables', '{%- data variables') + content = content.replaceAll('{%- データ variables', '{%- data variables') + content = content.replaceAll('{%- データ reusables', '{%- data reusables') + // `{% データ` followed by `.` (path operator) — translator dropped `variables`/`reusables` + content = content.replaceAll('{% データ.variables.', '{% data variables.') + content = content.replaceAll('{% データ.reusables.', '{% data reusables.') + // Generic Japanese `データ` data-tag normalizer. + // Matches `{%[-]?[ ]?データ[ ]?[再利用可能|再利用|変数|reusables|variables|...].PATH %}` + // and rewrites to `{%[-]? data .PATH %}` based on the keyword. + content = content.replace( + /\{%(-?)\s*データ\s*(再利用可能な?|再利用|reusables)\.([^\s%]+)\s*(-?)%\}/g, + (_m, dashOpen, _kw, path, dashClose) => `{%${dashOpen} data reusables.${path} ${dashClose}%}`, + ) + content = content.replace( + /\{%(-?)\s*データ\s*(変数|variables)\.([^\s%]+)\s*(-?)%\}/g, + (_m, dashOpen, _kw, path, dashClose) => `{%${dashOpen} data variables.${path} ${dashClose}%}`, + ) + // Bare `{%データ` / `{%- データ` followed by space + (variables|reusables) + content = content.replace( + /\{%(-?)\s*データ\s+(variables|reusables)\.([^\s%]+)\s*(-?)%\}/g, + (_m, dashOpen, kw, path, dashClose) => `{%${dashOpen} data ${kw}.${path} ${dashClose}%}`, + ) + // `{% メモ` capitalized variant + content = content.replaceAll('{% メモ -%}', '{%- note -%}') + content = content.replaceAll('{%- メモ -%}', '{%- note -%}') + // `{% ノート %}` — alternate Japanese for "note" + content = content.replaceAll('{% ノート %}', '{% note %}') + content = content.replaceAll('{%- ノート %}', '{%- note %}') + // `{% 終わり %}` / `{% 終了 %}` — Japanese "end" used as endif + content = content.replaceAll('{% 終わり %}', '{% endif %}') + content = content.replaceAll('{%- 終わり %}', '{%- endif %}') + content = content.replaceAll('{% 終了 %}', '{% endif %}') + content = content.replaceAll('{%- 終了 %}', '{%- endif %}') + // `{% 終了for %}` / `{% endforの場合 %}` — endfor variants + content = content.replaceAll('{% 終了for %}', '{% endfor %}') + content = content.replaceAll('{%- 終了for %}', '{%- endfor %}') + // Japanese `または` = "or", `かつ` / `および` = "and" inside ifversion/elsif/if + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?または[^%]*?-?%\}/g, (m) => + m.replace(/\s*または\s*/g, ' or '), + ) + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?かつ[^%]*?-?%\}/g, (m) => + m.replace(/\s*かつ\s*/g, ' and '), + ) + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?および[^%]*?-?%\}/g, (m) => + m.replace(/\s*および\s*/g, ' and '), + ) + // `{% 行ヘッダー %}` — "row headers" = rowheaders + content = content.replaceAll('{% 行ヘッダー %}', '{% rowheaders %}') + content = content.replaceAll('{%- 行ヘッダー %}', '{%- rowheaders %}') + // `{% 終了行ヘッダー %}` — "end row headers" = endrowheaders + content = content.replaceAll('{% 終了行ヘッダー %}', '{% endrowheaders %}') + content = content.replaceAll('{%- 終了行ヘッダー %}', '{%- endrowheaders %}') + // `{% ウィンドウ %}` / `{% ウィンドウズ %}` — "window/windows" = windows + content = content.replaceAll('{% ウィンドウ %}', '{% windows %}') + content = content.replaceAll('{%- ウィンドウ %}', '{%- windows %}') + content = content.replaceAll('{% ウィンドウズ %}', '{% windows %}') + content = content.replaceAll('{%- ウィンドウズ %}', '{%- windows %}') + // `{% Windowsターミナル %}` / `{% Windows ターミナル %}` — Windows terminal + content = content.replaceAll('{% Windowsターミナル %}', '{% windows %}') + content = content.replaceAll('{% Windows ターミナル %}', '{% windows %}') + // `{% indented_data_reference 再利用可能.X.Y spaces=N %}` — translated path + content = content.replace(/(\{%-?\s*indented_data_reference\s+)再利用可能\./g, '$1reusables.') } if (context.code === 'pt') { @@ -299,6 +428,96 @@ export function correctTranslatedContentStrings( '{% dados agrupados por categoria.complemento.audit_log.reference-grouped-by-category %}', '{% data reusables.audit_log.reference-grouped-by-category %}', ) + // Portuguese decimal comma in version numbers inside ifversion/elsif tags: `3,16` → `3.16` + content = content.replace(/\{%-? (?:ifversion|elsif) [^%]*?%\}/g, (match) => { + return match.replace(/(\d),(\d)/g, '$1.$2') + }) + // `{% para X em Y %}` — Portuguese "for X in Y" + content = content.replace(/\{%-? para (\w+) em /g, (match) => { + return match.replace(/para (\w+) em /, 'for $1 in ') + }) + // `{% reutilizáveis.X.Y %}` — translated reusables path with no `data` prefix + content = content.replaceAll('{% reutilizáveis.', '{% data reusables.') + content = content.replaceAll('{%- reutilizáveis.', '{%- data reusables.') + // `{% dados reusáveis.X.Y %}` — alternate Portuguese spelling for "reusables" + content = content.replaceAll('{% dados reusáveis.', '{% data reusables.') + content = content.replaceAll('{%- dados reusáveis.', '{%- data reusables.') + // `{% reusáveis.X.Y %}` — alternate without `data` prefix + content = content.replaceAll('{% reusáveis.', '{% data reusables.') + content = content.replaceAll('{%- reusáveis.', '{%- data reusables.') + // `{% dados.reutilizáveis.X.Y %}` — translator used `.` instead of space between + // "dados" (data) and "reutilizáveis" (reusables) + content = content.replaceAll('{% dados.reutilizáveis.', '{% data reusables.') + content = content.replaceAll('{%- dados.reutilizáveis.', '{%- data reusables.') + // `{% dados.reusáveis.` — same with alternate spelling + content = content.replaceAll('{% dados.reusáveis.', '{% data reusables.') + content = content.replaceAll('{%- dados.reusáveis.', '{%- data reusables.') + // `{% de data X` — translator inserted Portuguese preposition "de" (of/from) + // before `data variables` / `data reusables` + content = content.replaceAll('{% de data variables', '{% data variables') + content = content.replaceAll('{%- de data variables', '{%- data variables') + content = content.replaceAll('{% de data reusables', '{% data reusables') + content = content.replaceAll('{%- de data reusables', '{%- data reusables') + content = content.replaceAll('{% de dados reusables', '{% data reusables') + // `{% datavariables` — no space between "data" and "variables" (sometimes survives) + content = content.replaceAll('{% datavariables', '{% data variables') + content = content.replaceAll('{%- datavariables', '{%- data variables') + // `{% datas variables` / `{% datas reusables` — plural Portuguese form of "data" + content = content.replaceAll('{% datas variables', '{% data variables') + content = content.replaceAll('{%- datas variables', '{%- data variables') + content = content.replaceAll('{% datas reusables', '{% data reusables') + content = content.replaceAll('{%- datas reusables', '{%- data reusables') + // Word-order swap inside ifversion: `{% ghes ifversion %}` → `{% ifversion ghes %}` + content = content.replace( + /\{%(-?)\s*(fpt|ghec|ghes)\s+ifversion\s*%\}/g, + '{%$1 ifversion $2 %}', + ) + // With extra "de" word: `{% ghes de ifversion %}` → `{% ifversion ghes %}` + content = content.replace( + /\{%(-?)\s*(fpt|ghec|ghes)\s+de\s+ifversion\s*%\}/g, + '{%$1 ifversion $2 %}', + ) + // Mangled order: `{% %} de ghec ifversion` → `{% ifversion ghec %}` + content = content.replaceAll('{% %} de ghec ifversion', '{% ifversion ghec %}') + content = content.replaceAll('{% %} de ghes ifversion', '{% ifversion ghes %}') + content = content.replaceAll('{% %} de fpt ifversion', '{% ifversion fpt %}') + // `{% referência_dados_indentados ` — Portuguese translation of `indented_data_reference` + content = content.replaceAll('{% referência_dados_indentados ', '{% indented_data_reference ') + content = content.replaceAll('{%- referência_dados_indentados ', '{%- indented_data_reference ') + // Broad fallback: any remaining `{% dados ` / `{% Dados ` → `{% data ` + content = content.replace(/\{%(-?)\s*[Dd]ados\s+/g, '{%$1 data ') + // After broad fallback, translated path segments may remain. Catch the most common. + content = content.replace(/\{%(-?\s*)data reutilizáveis\./g, '{%$1data reusables.') + content = content.replace(/\{%(-?\s*)data variáveis\./g, '{%$1data variables.') + // `{% reutilizáveis.` / `{% variáveis.` (no `data` prefix) → add data + content = content.replace(/\{%(-?\s*)reutilizáveis\./g, '{%$1data reusables.') + content = content.replace(/\{%(-?\s*)variáveis\./g, '{%$1data variables.') + // Portuguese `ou` = "or" / `e` = "and" inside ifversion/elsif/if tags + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?\sou\s[^%]*?-?%\}/g, (m) => + m.replace(/\sou\s/g, ' or '), + ) + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?\se\s[^%]*?-?%\}/g, (m) => + m.replace(/\se\s/g, ' and '), + ) + // `{% senão %}` / `{% Senão %}` — Portuguese "else" + content = content.replaceAll('{% senão %}', '{% else %}') + content = content.replaceAll('{%- senão %}', '{%- else %}') + content = content.replaceAll('{% Senão %}', '{% else %}') + content = content.replaceAll('{% senao %}', '{% else %}') + content = content.replaceAll('{%- senao %}', '{%- else %}') + // `{% senão se ` / `{% senao se ` — "else if" = elsif + content = content.replaceAll('{% senão se ', '{% elsif ') + content = content.replaceAll('{%- senão se ', '{%- elsif ') + content = content.replaceAll('{% senao se ', '{% elsif ') + // `{% caso contrário %}` — alternate "otherwise" = else + content = content.replaceAll('{% caso contrário %}', '{% else %}') + content = content.replaceAll('{%- caso contrário %}', '{%- else %}') + // `{% observação %}` — "note" = note + content = content.replaceAll('{% observação %}', '{% note %}') + content = content.replaceAll('{%- observação %}', '{%- note %}') + // `{% modelo %}` / `{% modelo` — `template` (alias for `tool`)? Actually "modelo" + // appears as `{% modelo %}` orphaned. Drop unmatched bare `{% modelo %}` is + // risky; instead, leave as-is (Liquid will raise but rare). } if (context.code === 'zh') { @@ -323,10 +542,13 @@ export function correctTranslatedContentStrings( // Stray Chinese `,则为` ("then") merged with `{%` before HTML: `,则为 {%` → `` // The regex consumes the `<` to avoid producing a double `<<`. content = content.replace(/,则为 \{% { - return match.replace(/ 或 /g, ' or ') - }) + // Catch "或" / "和" between any plan names in ifversion/elsif/if tags + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?或[^%]*?-?%\}/g, (m) => + m.replace(/\s*或\s*/g, ' or '), + ) + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?和[^%]*?-?%\}/g, (m) => + m.replace(/\s*和\s*/g, ' and '), + ) // `{% 行标题 %}` — "row headers" = rowheaders content = content.replaceAll('{% 行标题 %}', '{% rowheaders %}') content = content.replaceAll('{%- 行标题 %}', '{%- rowheaders %}') @@ -337,6 +559,52 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{%- Windows 操作系统 %}', '{%- windows %}') // `{% Windows终端 %}` — "Windows terminal" = windows platform tag content = content.replaceAll('{% Windows终端 %}', '{% windows %}') + // `{% 桌面 %}` — Chinese "desktop" = desktop platform tag + content = content.replaceAll('{% 桌面 %}', '{% desktop %}') + content = content.replaceAll('{%- 桌面 %}', '{%- desktop %}') + // `{% 行标头 %}` / `{% 行标题 %}` — alternate Chinese for "row headers" + content = content.replaceAll('{% 行标头 %}', '{% rowheaders %}') + content = content.replaceAll('{%- 行标头 %}', '{%- rowheaders %}') + content = content.replaceAll('{% 行标题 %}', '{% rowheaders %}') + content = content.replaceAll('{%- 行标题 %}', '{%- rowheaders %}') + // `{% 结束行标题 %}` / `{% 结束行标头 %}` / `{% 结束行头 %}` — endrowheaders + content = content.replaceAll('{% 结束行标题 %}', '{% endrowheaders %}') + content = content.replaceAll('{%- 结束行标题 %}', '{%- endrowheaders %}') + content = content.replaceAll('{% 结束行标头 %}', '{% endrowheaders %}') + content = content.replaceAll('{%- 结束行标头 %}', '{%- endrowheaders %}') + content = content.replaceAll('{% 结束行头 %}', '{% endrowheaders %}') + content = content.replaceAll('{%- 结束行头 %}', '{%- endrowheaders %}') + // `{% 行标题结束 %}` — order swap (rowheaders + end) + content = content.replaceAll('{% 行标题结束 %}', '{% endrowheaders %}') + content = content.replaceAll('{%- 行标题结束 %}', '{%- endrowheaders %}') + // Capitalized `{% Variables.X %}` / `{% Reusables.X %}` — translator title-cased + content = content.replaceAll('{% data Variables.', '{% data variables.') + content = content.replaceAll('{% data Reusables.', '{% data reusables.') + content = content.replaceAll('{%- data Variables.', '{%- data variables.') + content = content.replaceAll('{%- data Reusables.', '{%- data reusables.') + // `{% 否则如果 ` — "otherwise if" = elsif + content = content.replaceAll('{% 否则如果 ', '{% elsif ') + content = content.replaceAll('{%- 否则如果 ', '{%- elsif ') + // `{% 结束 %}` / `{% 结尾 %}` — Chinese "end" = endif + content = content.replaceAll('{% 结束 %}', '{% endif %}') + content = content.replaceAll('{%- 结束 %}', '{%- endif %}') + content = content.replaceAll('{% 结尾 %}', '{% endif %}') + content = content.replaceAll('{%- 结尾 %}', '{%- endif %}') + // `{% 结束for %}` — end + for + content = content.replaceAll('{% 结束for %}', '{% endfor %}') + content = content.replaceAll('{%- 结束for %}', '{%- endfor %}') + // `{% 结束if %}` / `{% endif的话 %}` — endif variants + content = content.replaceAll('{% 结束if %}', '{% endif %}') + content = content.replaceAll('{%- 结束if %}', '{%- endif %}') + // Broad fallback: any remaining `{% 数据 ` → `{% data ` + content = content.replace(/\{%(-?)\s*数据\s+/g, '{%$1 data ') + // `{% indented_data_reference 可重用|可复用|可重用项|可重用组件|可复用项.X.Y spaces=N %}` + // — translator converted the `reusables` path prefix into Chinese. Collapse + // any `可(重|复)用[项|组件|s]?.` prefix into `reusables.`. + content = content.replace( + /(\{%-?\s*indented_data_reference\s+)可(?:重|复)用(?:项|组件|s)?\./g, + '$1reusables.', + ) } if (context.code === 'ru') { @@ -380,6 +648,10 @@ export function correctTranslatedContentStrings( content = content.replace(/\{%-? (?:ifversion|elsif|if) [^%]*?или[^%]*?%\}/g, (match) => { return match.replace(/ или /g, ' or ') }) + // Russian decimal comma in version numbers inside ifversion/elsif tags: `3,18` → `3.18` + content = content.replace(/\{%-? (?:ifversion|elsif) [^%]*?%\}/g, (match) => { + return match.replace(/(\d),(\d)/g, '$1.$2') + }) content = content.replaceAll('{% конечным %}', '{% endif %}') content = content.replaceAll('{%- конечным %}', '{%- endif %}') // `{%- конец %}` — dash-trimmed form of "end" = endif @@ -421,6 +693,14 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{%- примечание %}', '{%- note %}') content = content.replaceAll('{% конечных головщиков %}', '{% endrowheaders %}') content = content.replaceAll('{% данных для повторного использования.', '{% data reusables.') + // `{% indented_data_reference повторн... .X.Y spaces=N %}` — translator + // converted the `reusables` path prefix into Russian (with spaces inside), + // which breaks the indented_data_reference parser. Collapse any + // `повторн[...][ ]*.` prefix into `reusables.`. + content = content.replace( + /(\{%-?\s*indented_data_reference\s+)повторн[а-яё]*(?:\s+[а-яё]+)*\./g, + '$1reusables.', + ) content = content.replaceAll('{% еще %}', '{% else %}') content = content.replaceAll('{%- еще %}', '{%- else %}') content = content.replaceAll('{% ещё %}', '{% else %}') @@ -476,9 +756,102 @@ export function correctTranslatedContentStrings( '{% octicon "организация" aria-hidden="true" aria-label="organization" %}', '{% octicon "organization" aria-hidden="true" aria-label="organization" %}', ) + // `{% Эльсиф CONDITION %}` — transliteration of "elsif" with a condition + content = content.replace(/\{%(-?)\s*Эльсиф\s+/g, '{%$1 elsif ') + // `{% для X в Y %}` — Russian "for X in Y" + content = content.replace(/\{%-?\s*для\s+(\w+)\s+в\s+/g, (match) => { + const dash = match.startsWith('{%-') ? '{%-' : '{%' + return match.replace(/^\{%-?\s*для\s+(\w+)\s+в\s+/, `${dash} for $1 in `) + }) + // `, а не ghes` — Russian "and not ghes" inside ifversion expressions + content = content.replace(/\{%-? (?:ifversion|elsif|if) [^%]*?, а не [^%]*?%\}/g, (match) => { + return match.replace(/, а не /g, ' and not ') + }) + // `{% ifversion ghes не ` — `не` ("not") inside ifversion + content = content.replace(/\{%-? (?:ifversion|elsif|if) [^%]*?\sне\s[^%]*?%\}/g, (match) => { + return match.replace(/ не /g, ' not ') + }) + // `aria-label="autoTITLE"` — "AUTOTITLE" was lowercased by translator + content = content.replaceAll('aria-label="autoTITLE"', 'aria-label="AUTOTITLE"') + // `{% эндраw %}` / `{% эндраw -%}` — transliterated endraw + content = content.replaceAll('{% эндраw %}', '{% endraw %}') + content = content.replaceAll('{%- эндраw %}', '{%- endraw %}') + content = content.replaceAll('{% эндраw -%}', '{% endraw -%}') + // `{% эндесктоп %}` — transliterated enddesktop + content = content.replaceAll('{% эндесктоп %}', '{% enddesktop %}') + content = content.replaceAll('{%- эндесктоп %}', '{%- enddesktop %}') + // `{% эндекклипс %}` / `{% эндеклипс %}` — transliterated endeclipse + content = content.replaceAll('{% эндеклипс %}', '{% endeclipse %}') + content = content.replaceAll('{%- эндеклипс %}', '{%- endeclipse %}') + content = content.replaceAll('{% эндекклипс %}', '{% endeclipse %}') + // `{% endекклипс %}` — partial transliteration + content = content.replaceAll('{% endекклипс %}', '{% endeclipse %}') + // `{%- лицензия %}` — Russian "license"... actually this is a feature flag value, not a tag + // Translator-formatted "Russian smart quotes" inside Liquid tags: «X» → "X" + content = content.replace(/(\{%-?\s*[a-z]+\s+)«([^»]*)»/g, '$1"$2"') + // `{% ifversion fpt or ghec or ghes >NUMBER %}` — when range value is wrapped in + // Cyrillic chars or letter "о" instead of "0", normalize digits + content = content.replace(/\{%-? (?:ifversion|elsif) [^%]*?[<>=][^%]*?%\}/g, (match) => { + // Cyrillic 'о' (U+043E) often replaces ASCII '0' (U+0030) + return match.replace(/(\d)\s*о/g, '$10').replace(/о\s*(\d)/g, '0$1') + }) + + // Word-order swap: translator placed plan name BEFORE `ifversion`, e.g. + // `{% ghes ifversion %}` → `{% ifversion ghes %}`, + // `{% ghes ifversion < 3,14 %}` → `{% ifversion ghes < 3.14 %}` + content = content.replace( + /\{%(-?)\s*(fpt|ghec|ghes|ghae|ghecom)\s+ifversion\s*([^%]*?)\s*-?%\}/g, + (_m, dash, plan, rest) => { + const fixedRest = rest.replace(/(\d),(\d)/g, '$1.$2') + const trimmed = fixedRest.trim() + return `{%${dash} ifversion ${plan}${trimmed ? ` ${trimmed}` : ''} %}` + }, + ) + // Missing `ifversion` prefix: `{% ghes или ghec %}` → `{% ifversion ghes or ghec %}` + content = content.replace( + /\{%(-?)\s*(fpt|ghec|ghes|ghae|ghecom)\s+или\s+(fpt|ghec|ghes|ghae|ghecom)\s*-?%\}/g, + '{%$1 ifversion $2 or $3 %}', + ) + // Same pattern with "and" / "и" + content = content.replace( + /\{%(-?)\s*(fpt|ghec|ghes|ghae|ghecom)\s+и\s+(fpt|ghec|ghes|ghae|ghecom)\s*-?%\}/g, + '{%$1 ifversion $2 and $3 %}', + ) + // `{% ghes version %}` (translator dropped `ifversion`, added "version") + content = content.replace( + /\{%(-?)\s*(fpt|ghec|ghes|ghae|ghecom)\s+version\s*-?%\}/g, + '{%$1 ifversion $2 %}', + ) + + // Russian "остальных" / "иначе" / "ещё" / "иначе если" → else / elsif + content = content.replaceAll('{% остальных %}', '{% else %}') + content = content.replaceAll('{%- остальных %}', '{%- else %}') + content = content.replaceAll('{% иначе %}', '{% else %}') + content = content.replaceAll('{%- иначе %}', '{%- else %}') + content = content.replaceAll('{% ещё %}', '{% else %}') + content = content.replaceAll('{%- ещё %}', '{%- else %}') + content = content.replace(/\{%(-?)\s*иначе если\s+/g, '{%$1 elsif ') + // Russian `или` = "or", `и` = "and" inside ifversion/elsif/if tags + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?или[^%]*?-?%\}/g, (m) => + m.replace(/\s*или\s*/g, ' or '), + ) + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?\sи\s[^%]*?-?%\}/g, (m) => + m.replace(/\sи\s/g, ' and '), + ) } if (context.code === 'fr') { + // `{% sinon %}` — "otherwise" = else + content = content.replaceAll('{% sinon %}', '{% else %}') + content = content.replaceAll('{%- sinon %}', '{%- else %}') + // `{% référentiel ifversion ` — translator inserted "référentiel" (repository) before ifversion + content = content.replaceAll('{% référentiel ifversion ', '{% ifversion ') + content = content.replaceAll('{%- référentiel ifversion ', '{%- ifversion ') + // Standalone `{% référentiel %}` / `{% paramètres %}` / `{% product %}` are stray + // translation residue with no Liquid meaning — strip them. + content = content.replace(/\{%-?\s*référentiel\s*-?%\}/g, '') + content = content.replace(/\{%-?\s*paramètres\s*-?%\}/g, '') + content = content.replace(/\{%-?\s*product\s*-?%\}/g, '') content = content.replaceAll('{% données variables', '{% data variables') content = content.replaceAll('{% données réutilisables.', '{% data reusables.') content = content.replaceAll('{% variables de données.', '{% data variables.') @@ -567,6 +940,42 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{%- endif %}', '') content = content.replaceAll('{%- endif -%}', '') } + // `{% pour X dans Y %}` — French "for X in Y" + content = content.replace(/\{%(-?)\s*pour\s+(\w+)\s+dans\s+/g, '{%$1 for $2 in ') + // `{% pour le modèle dans Y %}` — "for the model in Y" = `for model in Y` + content = content.replace(/\{%(-?)\s*pour\s+le\s+modèle\s+dans\s+/g, '{%$1 for model in ') + // `{% pour chaque X dans Y %}` — "for each X in Y" = `for X in Y` + content = content.replace(/\{%(-?)\s*pour\s+chaque\s+(\w+)\s+dans\s+/g, '{%$1 for $2 in ') + // `{% des données variables.` — "of the data variables" prefix + content = content.replaceAll('{% des données variables.', '{% data variables.') + content = content.replaceAll('{%- des données variables.', '{%- data variables.') + content = content.replaceAll('{% des data variables.', '{% data variables.') + content = content.replaceAll('{%- des data variables.', '{%- data variables.') + // `{% variables de données.` — already handled via line 484 + // `{% assigner X = Y %}` — "to assign" = assign + content = content.replaceAll('{% assigner ', '{% assign ') + content = content.replaceAll('{%- assigner ', '{%- assign ') + // `{% quand "X" %}` — French "when" inside case/when blocks + content = content.replace(/\{%(-?)\s*quand\s+/g, '{%$1 when ') + // `{% endcase %}` variants + content = content.replaceAll('{% fincas %}', '{% endcase %}') + content = content.replaceAll('{%- fincas %}', '{%- endcase %}') + // `{% réutilisables.X.Y %}` — translated reusables path with no `data` prefix + content = content.replaceAll('{% réutilisables.', '{% data reusables.') + content = content.replaceAll('{%- réutilisables.', '{%- data reusables.') + // Broad fallback: any remaining `{% données ` → `{% data ` (runs LAST so specific + // path-fixing rules above get first crack). + content = content.replace(/\{%(-?)\s*données\s+/g, '{%$1 data ') + // After broad fallback, common translated path segments may remain. + content = content.replace(/\{%(-?\s*)data réutilisables\./g, '{%$1data reusables.') + content = content.replace(/\{%(-?\s*)data variables de\./g, '{%$1data variables.') + // French `ou` = "or", `et` = "and" inside ifversion/elsif/if tags + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?\sou\s[^%]*?-?%\}/g, (m) => + m.replace(/\sou\s/g, ' or '), + ) + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?\set\s[^%]*?-?%\}/g, (m) => + m.replace(/\set\s/g, ' and '), + ) } if (context.code === 'ko') { @@ -581,10 +990,44 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{%- 참고 %}', '{%- note %}') content = content.replaceAll('{% 원시 %}', '{% raw %}') content = content.replaceAll('{%- 원시 %}', '{%- raw %}') + // Translated tag name `{% 들여쓰기_데이터_참조 ... %}` → `{% indented_data_reference ... %}` + content = content.replaceAll('{% 들여쓰기_데이터_참조 ', '{% indented_data_reference ') + content = content.replaceAll('{%- 들여쓰기_데이터_참조 ', '{%- indented_data_reference ') + // `{% 옥티콘 "X" ... %}` → `{% octicon "X" ... %}` + content = content.replaceAll('{% 옥티콘 ', '{% octicon ') + content = content.replaceAll('{%- 옥티콘 ', '{%- octicon ') + // `{% 행 머리글 %}` → `{% rowheaders %}` + content = content.replaceAll('{% 행 머리글 %}', '{% rowheaders %}') + content = content.replaceAll('{%- 행 머리글 %}', '{%- rowheaders %}') + content = content.replaceAll('{% 행머리글 %}', '{% rowheaders %}') + content = content.replaceAll('{%- 행머리글 %}', '{%- rowheaders %}') + // `{% 엔드맥 %}` → `{% endmac %}` + content = content.replaceAll('{% 엔드맥 %}', '{% endmac %}') + content = content.replaceAll('{%- 엔드맥 %}', '{%- endmac %}') + // `{% 윈도우즈 %}` / `{% 윈도우 %}` → `{% windows %}` + content = content.replaceAll('{% 윈도우즈 %}', '{% windows %}') + content = content.replaceAll('{%- 윈도우즈 %}', '{%- windows %}') + content = content.replaceAll('{% 윈도우 %}', '{% windows %}') + content = content.replaceAll('{%- 윈도우 %}', '{%- windows %}') + // `{% 데이터 재사용 ` (no period) — variant of `{% data reusables` + content = content.replaceAll('{% 데이터 재사용가능항목.', '{% data reusables.') + content = content.replaceAll('{% 데이터 재사용 가능 항목.', '{% data reusables.') + content = content.replaceAll('{% 데이터 재사용.', '{% data reusables.') + content = content.replaceAll('{% 데이터 재사용 ', '{% data reusables.') + // `{% indented_data_reference 재사용...` — translated `reusables` path prefix + content = content.replace( + /(\{%-?\s*indented_data_reference\s+)재사용(?:\s+가능)?(?:\s+항목)?\./g, + '$1reusables.', + ) // Catch "또는" between any plan names in ifversion/elsif/if tags - content = content.replace(/\{%-? (?:ifversion|elsif|if) [^%]*?또는[^%]*?%\}/g, (match) => { - return match.replace(/ 또는 /g, ' or ') - }) + // (handles `fpt 또는 ghec`, `ghes > 3.15`, etc.) + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?또는[^%]*?-?%\}/g, (m) => + m.replace(/\s*또는\s*/g, ' or '), + ) + // Korean "그리고" / "와" / "과" = "and" inside ifversion/elsif/if tags + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?그리고[^%]*?-?%\}/g, (m) => + m.replace(/\s*그리고\s*/g, ' and '), + ) // `{% 그렇지 않으면 %}` — "otherwise" = else content = content.replaceAll('{% 그렇지 않으면 %}', '{% else %}') content = content.replaceAll('{%- 그렇지 않으면 %}', '{%- else %}') @@ -612,6 +1055,39 @@ export function correctTranslatedContentStrings( // `{% 주석 끝 %}` — Korean "주석 끝" (note end) = endnote content = content.replaceAll('{% 주석 끝 %}', '{% endnote %}') content = content.replaceAll('{%- 주석 끝 %}', '{%- endnote %}') + // `{% 데이터.X %}` — translator dropped `variables`/`reusables` + content = content.replaceAll('{% 데이터.variables.', '{% data variables.') + content = content.replaceAll('{% 데이터.reusables.', '{% data reusables.') + // `{% 데이터variables` / `{% 데이터reusables` (no space) + content = content.replaceAll('{% 데이터variables', '{% data variables') + content = content.replaceAll('{% 데이터reusables', '{% data reusables') + content = content.replaceAll('{%- 데이터variables', '{%- data variables') + content = content.replaceAll('{%- 데이터reusables', '{%- data reusables') + // `{% 재사용 가능 항목.` — Korean for "reusables" with no `data` prefix + content = content.replaceAll('{% 재사용 가능 항목.', '{% data reusables.') + content = content.replaceAll('{%- 재사용 가능 항목.', '{%- data reusables.') + // `{% 재사용 가능.` — alternate + content = content.replaceAll('{% 재사용 가능.', '{% data reusables.') + content = content.replaceAll('{%- 재사용 가능.', '{%- data reusables.') + // `{% 데이터 재사용 가능.` / `{% 데이터 재사용 가능 항목.` — full Korean for "data reusables" + content = content.replaceAll('{% 데이터 재사용 가능 항목.', '{% data reusables.') + content = content.replaceAll('{%- 데이터 재사용 가능 항목.', '{%- data reusables.') + content = content.replaceAll('{% 데이터 재사용 가능.', '{% data reusables.') + content = content.replaceAll('{%- 데이터 재사용 가능.', '{%- data reusables.') + // Korean "if" / "elsif" word translations + // `{% 만약 X %}` / `{% 만일 X %}` — "if" in Korean + content = content.replace(/\{%-?\s*만약\s+/g, (m) => + m.startsWith('{%-') ? '{%- if ' : '{% if ', + ) + content = content.replace(/\{%-?\s*만일\s+/g, (m) => + m.startsWith('{%-') ? '{%- if ' : '{% if ', + ) + // Korean "for X in Y" → 위해/대해/안에 patterns + // `{% 위해 X 안에 Y %}` — best-effort for-loop + content = content.replace(/\{%(-?)\s*위해\s+(\w+)\s+안에\s+/g, '{%$1 for $2 in ') + // `{% Variable.` (capital V) — variant + content = content.replaceAll('{% Variable.', '{% data variables.') + content = content.replaceAll('{%- Variable.', '{%- data variables.') } if (context.code === 'de') { @@ -725,10 +1201,234 @@ export function correctTranslatedContentStrings( '{%- ifversion-Sicherheitskampagnen %}', '{%- ifversion security-campaigns %}', ) + // `{% Webseite data variables` / `{%Webseite data variables` — translator inserted + // German "Webseite" (website) before `data variables`. Strip it. + content = content.replaceAll('{%Webseite data variables', '{% data variables') + content = content.replaceAll('{% Webseite data variables', '{% data variables') + content = content.replaceAll('{%- Webseite data variables', '{%- data variables') + // `{% Daten nur variables` — "data only variables" (translator inserted "nur") + content = content.replaceAll('{% Daten nur variables', '{% data variables') + content = content.replaceAll('{%- Daten nur variables', '{%- data variables') + // `{% Dateneinstellungen variables` — "data settings variables" (compound) = data + content = content.replaceAll('{% Dateneinstellungen variables', '{% data variables') + content = content.replaceAll('{%- Dateneinstellungen variables', '{%- data variables') + // `{% Datenpaket variables` — "data package variables" (compound) = data + content = content.replaceAll('{% Datenpaket variables', '{% data variables') + content = content.replaceAll('{%- Datenpaket variables', '{%- data variables') + // `{% datan variables` — typo of "Daten" + content = content.replaceAll('{% datan variables', '{% data variables') + content = content.replaceAll('{%- datan variables', '{%- data variables') + // `{%-Daten-variables` and `{%-Datenvariablen` (no space) handled at line 647-648 + // Add the `{%-Datenvariablen.` no-space variant + content = content.replaceAll('{%-Datenvariablen.', '{%- data variables.') + // Broad fallback: any remaining `{% Daten ` / `{% daten ` → `{% data ` + // Runs LAST so specific path-fixing rules above get first crack. + content = content.replace(/\{%(-?)\s*[Dd]aten\s+/g, '{%$1 data ') + // After broad fallback, translated path segments may remain in `{% data X.Y %}` + // where X is German. Catch the most common: `wiederverwendbar.` → `reusables.` + content = content.replace( + /\{%(-?\s*)data wiederverwendbar(?:e|en|ens)?\./g, + '{%$1data reusables.', + ) + content = content.replace(/\{%(-?\s*)data Variablen\./g, '{%$1data variables.') + // German `oder` = "or", `und` = "and" inside ifversion/elsif/if tags + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?\soder\s[^%]*?-?%\}/g, (m) => + m.replace(/\soder\s/g, ' or '), + ) + content = content.replace(/\{%-?\s+(?:ifversion|elsif|if)\s+[^%]*?\sund\s[^%]*?-?%\}/g, (m) => + m.replace(/\sund\s/g, ' and '), + ) + // Translated tag name `{% eingerucktes_datenverweis ... %}` → `{% indented_data_reference ... %}` + content = content.replaceAll('{% eingerucktes_datenverweis ', '{% indented_data_reference ') + content = content.replaceAll('{%- eingerucktes_datenverweis ', '{%- indented_data_reference ') } // --- Generic fixes (all languages) --- + // Inside ANY Liquid tag `{% ... %}` (including `{% octicon ... %}`, + // `{% data ... %}`, `{% assign ... %}` etc.), normalize typographic + // quotation marks back to ASCII straight quotes. Translators + // frequently smart-quote attribute values, which breaks the parser. + // Handles: « » (guillemets, fr/ru/es/it), “ ” (CJK double quotes, + // U+201C/U+201D, used in zh/ko/ja), „ “ ‚ ‘ (German low-9/high-6 + // quotes, U+201E/U+201C/U+201A/U+2018) and ' ' (curly singles). + content = content.replace(/\{%-?[^%]*?-?%\}/g, (match) => { + return match.replace(/[«»“”„]/g, '"').replace(/[‘’‚]/g, "'") + }) + + // Inside `{% ifversion|elsif|if ... %}` collapse runs of internal whitespace + // to a single space. Translators occasionally introduce double spaces + // (e.g. `{% ifversion fpt or ghec %}`), and although a single trailing + // space is fine, some patterns trip the parser. Safe to apply globally. + content = content.replace( + /\{%(-?)\s+(ifversion|elsif|if)\s+([^%]*?)\s*(-?)%\}/g, + (_m, dashOpen, tag, body, dashClose) => + `{%${dashOpen} ${tag} ${body.replace(/\s+/g, ' ').trim()} ${dashClose}%}`, + ) + + // Octicon icon-name English-content recovery. Translators often translate + // the icon name itself (e.g. `{% octicon "карандаш" %}` for "pencil"). The + // Octicon parser will then either fail with "Octicon X does not exist" or + // — because its regex is not anchored — match a later quoted attribute value + // such as `aria-hidden="true"` and report "Octicon true does not exist". + // Recover the original icon name by position when the english content is + // available and contains the same number of `{% octicon "" ... %}` + // tags as the translation. + if (englishContent && content.includes('{% octicon ')) { + const englishNames: string[] = [] + for (const m of englishContent.matchAll(/\{%-?\s*octicon\s+"([^"]*)"/g)) { + englishNames.push(m[1]) + } + // Count translated octicon tags (whether or not they have a quoted name). + const translatedAll = [...content.matchAll(/\{%-?\s*octicon\b[^%]*%\}/g)] + const translatedMatches = [...content.matchAll(/\{%-?\s*octicon\s+"([^"]*)"/g)] + if ( + englishNames.length > 0 && + translatedMatches.length === englishNames.length && + translatedMatches.some((m, i) => m[1] !== englishNames[i]) + ) { + let i = 0 + content = content.replace( + /(\{%-?\s*octicon\s+")([^"]*)(")/g, + (_match, prefix, _name, suffix) => { + const englishName = englishNames[i++] + return `${prefix}${englishName}${suffix}` + }, + ) + } else if (englishNames.length > 0) { + // Counts differ. Replace any non-ASCII icon name with a positional + // English fallback (clamped to valid range). Real octicon names are + // always ASCII, so non-ASCII is always wrong. + let i = 0 + content = content.replace( + /(\{%-?\s*octicon\s+")([^"]*)(")/g, + (match, prefix, name, suffix) => { + // eslint-disable-next-line no-control-regex + if (/[^\x00-\x7F]/.test(name)) { + const englishName = englishNames[Math.min(i, englishNames.length - 1)] + i++ + return `${prefix}${englishName}${suffix}` + } + i++ + return match + }, + ) + } + + // Translators sometimes drop the `"name"` first argument entirely: + // `{% octicon aria-label="..." %}` instead of `{% octicon "copy" aria-label="..." %}`. + // When tag counts match the English version, recover by injecting the + // positional English icon name. + if (englishNames.length > 0 && translatedAll.length === englishNames.length) { + let i = 0 + content = content.replace( + /(\{%-?\s*octicon)(\s+)([^"%][^%]*?)(\s*-?%\})/g, + (_m, head, ws, body, tail) => { + const name = englishNames[i++] + return `${head}${ws}"${name}" ${body.trim()} ${tail.trimStart()}` + }, + ) + } + } + + // After octicon recovery, the surrounding `{% octicon "X" ... %}` may still + // contain translated `aria-label` values. That's OK — Liquid parses key=value + // tokens regardless of non-ASCII content in the value. + + // `{% indented_data_reference spaces=N %}` recovery. Translators + // sometimes translate the reusables path (e.g. `재사용.X.Y` for ko, + // `wiederverwendbar.X.Y` for de) or insert internal spaces (ru: + // `повторно используемых пространств.X.Y`), which breaks the parser + // ("'spaces=NUMBER' must include a number"). Recover by replacing the + // entire path positionally from the English version when counts match. + if (englishContent && content.includes('{% indented_data_reference ')) { + const englishArgs: string[] = [] + for (const m of englishContent.matchAll( + /\{%-?\s*indented_data_reference\s+([^%]*?)\s*-?%\}/g, + )) { + englishArgs.push(m[1].trim()) + } + const translatedMatches = [ + ...content.matchAll(/\{%-?\s*indented_data_reference\s+([^%]*?)\s*-?%\}/g), + ] + if ( + englishArgs.length > 0 && + translatedMatches.length === englishArgs.length && + translatedMatches.some((m, i) => m[1].trim() !== englishArgs[i]) + ) { + let i = 0 + content = content.replace( + /(\{%-?\s*indented_data_reference\s+)([^%]*?)(\s*-?%\})/g, + (_match, prefix, _args, suffix) => { + const englishArg = englishArgs[i++] + return `${prefix}${englishArg}${suffix}` + }, + ) + } else if (englishArgs.length > 0) { + // Counts differ. Replace any args containing non-ASCII characters with + // the positional English fallback (clamped). Valid args are always ASCII. + let i = 0 + content = content.replace( + /(\{%-?\s*indented_data_reference\s+)([^%]*?)(\s*-?%\})/g, + (match, prefix, args, suffix) => { + // eslint-disable-next-line no-control-regex + if (/[^\x00-\x7F]/.test(args)) { + const englishArg = englishArgs[Math.min(i, englishArgs.length - 1)] + i++ + return `${prefix}${englishArg}${suffix}` + } + i++ + return match + }, + ) + } + } + + // `{% data %}` recovery. Translators sometimes translate the path + // segments (e.g. `数据.可重用.X.Y` for zh, `재사용 가능.X.Y` for ko, + // `wiederverwendbar.X.Y` for de). When the data tag itself parses but the + // resolved path doesn't exist, we get "Can't find the key 'X' in the scope". + // Recover by replacing the entire path positionally from the English version + // when counts match, or by swapping any path containing non-ASCII characters + // with the positional English fallback. Valid `{% data %}` paths are always + // ASCII (lowercase letters, digits, dots, underscores, hyphens). + if (englishContent && content.includes('{% data ')) { + const englishArgs: string[] = [] + for (const m of englishContent.matchAll(/\{%-?\s*data\s+([^%]*?)\s*-?%\}/g)) { + englishArgs.push(m[1].trim()) + } + const translatedMatches = [...content.matchAll(/\{%-?\s*data\s+([^%]*?)\s*-?%\}/g)] + if ( + englishArgs.length > 0 && + translatedMatches.length === englishArgs.length && + translatedMatches.some((m, i) => m[1].trim() !== englishArgs[i]) + ) { + let i = 0 + content = content.replace( + /(\{%-?\s*data\s+)([^%]*?)(\s*-?%\})/g, + (_match, prefix, _args, suffix) => { + const englishArg = englishArgs[i++] + return `${prefix}${englishArg}${suffix}` + }, + ) + } else if (englishArgs.length > 0) { + let i = 0 + content = content.replace( + /(\{%-?\s*data\s+)([^%]*?)(\s*-?%\})/g, + (match, prefix, args, suffix) => { + // eslint-disable-next-line no-control-regex + if (/[^\x00-\x7F]/.test(args)) { + const englishArg = englishArgs[Math.min(i, englishArgs.length - 1)] + i++ + return `${prefix}${englishArg}${suffix}` + } + i++ + return match + }, + ) + } + } + // Strip leaked LLM sentinel markers (e.g. `<|endoftext|>`) that // occasionally survive the translation pipeline. Replace the marker // and any surrounding whitespace with a single space so adjacent @@ -957,5 +1657,108 @@ export function correctTranslatedContentStrings( content = content.replaceAll('{%endraw %}', '{% endraw %}') content = content.replaceAll('{%endraw -%}', '{% endraw -%}') + // Strip stray closing-only Liquid tags that have no matching opener anywhere + // in the content. Translators sometimes insert spurious closers (e.g. an + // extra `{% endif %}`) when they re-arrange paragraphs. We only remove a + // stray closer when there are strictly more closers than possible openers, + // and we remove from the END of the content first (most likely the stray). + // + // We treat each closer with its set of valid opener regexes: + // endif ← `{% if `, `{% ifversion ` + // endfor ← `{% for ` + // endraw ← `{% raw %}` + // endcase ← `{% case ` + // endcomment ← `{% comment %}` + // endcapture ← `{% capture ` + // endnote ← `{% note %}` + // endwarning ← `{% warning %}` + // endtip ← `{% tip %}` + // endcaution ← `{% caution %}` + // endimportant← `{% important %}` + // endrowheaders← `{% rowheaders %}` + // enddesktop ← `{% desktop %}` + // endmac ← `{% mac %}` + // endwebui ← `{% webui %}` + // endwindowsterminal ← `{% windowsterminal %}` + // endwindows ← `{% windows %}` + // endlinux ← `{% linux %}` + // endeclipse ← `{% eclipse %}` + // endjetbrains← `{% jetbrains %}` + // endvscode ← `{% vscode %}` + // endvisualstudio ← `{% visualstudio %}` + // endprompt ← `{% prompt %}` + // endmobile ← `{% mobile %}` + // endcli ← `{% cli %}` + // endcurl ← `{% curl %}` + // endindented_data_reference ← `{% indented_data_reference ` + // + // `else` / `elsif` are stripped only when no `if`/`ifversion` opener exists. + const closerToOpeners: Array<[string, RegExp]> = [ + ['endif', /\{%-?\s*(?:if|ifversion)\s/], + ['endfor', /\{%-?\s*for\s/], + ['endraw', /\{%-?\s*raw\s*-?%\}/], + ['endcase', /\{%-?\s*case\s/], + ['endcomment', /\{%-?\s*comment\s*-?%\}/], + ['endcapture', /\{%-?\s*capture\s/], + ['endnote', /\{%-?\s*note\s*-?%\}/], + ['endwarning', /\{%-?\s*warning\s*-?%\}/], + ['endtip', /\{%-?\s*tip\s*-?%\}/], + ['endcaution', /\{%-?\s*caution\s*-?%\}/], + ['endimportant', /\{%-?\s*important\s*-?%\}/], + ['endrowheaders', /\{%-?\s*rowheaders\s*-?%\}/], + ['enddesktop', /\{%-?\s*desktop\s*-?%\}/], + ['endmac', /\{%-?\s*mac\s*-?%\}/], + ['endwebui', /\{%-?\s*webui\s*-?%\}/], + ['endwindowsterminal', /\{%-?\s*windowsterminal\s*-?%\}/], + ['endwindows', /\{%-?\s*windows\s*-?%\}/], + ['endlinux', /\{%-?\s*linux\s*-?%\}/], + ['endeclipse', /\{%-?\s*eclipse\s*-?%\}/], + ['endjetbrains', /\{%-?\s*jetbrains\s*-?%\}/], + ['endvscode', /\{%-?\s*vscode\s*-?%\}/], + ['endvisualstudio', /\{%-?\s*visualstudio\s*-?%\}/], + ['endprompt', /\{%-?\s*prompt\s*-?%\}/], + ['endmobile', /\{%-?\s*mobile\s*-?%\}/], + ['endcli', /\{%-?\s*cli\s*-?%\}/], + ['endcurl', /\{%-?\s*curl\s*-?%\}/], + ['endindented_data_reference', /\{%-?\s*indented_data_reference\s/], + ] + + // Tests that exercise individual transformations in isolation can opt out + // of the orphan-closer stripping below (which is tested separately). + if (context.skipOrphanStripping) { + return content + } + + for (const [closer, openerRegex] of closerToOpeners) { + const closerRegex = new RegExp(`\\{%-?\\s*${closer}\\s*-?%\\}`, 'g') + const closers = content.match(closerRegex) + if (!closers) continue + // Count openers using a global version of the opener regex. + const globalOpener = new RegExp(openerRegex.source, `${openerRegex.flags}g`) + const openers = content.match(globalOpener) + const openerCount = openers ? openers.length : 0 + const closerCount = closers.length + if (closerCount <= openerCount) continue + // Remove (closerCount - openerCount) closers, starting from the LAST. + let toRemove = closerCount - openerCount + const positions: Array<{ start: number; end: number }> = [] + closerRegex.lastIndex = 0 + let m: RegExpExecArray | null + while ((m = closerRegex.exec(content)) !== null) { + positions.push({ start: m.index, end: m.index + m[0].length }) + } + // Remove from the END (translator-appended extras). + for (let i = positions.length - 1; i >= 0 && toRemove > 0; i--, toRemove--) { + const { start, end } = positions[i] + content = content.slice(0, start) + content.slice(end) + } + } + + // Strip stray `else` / `elsif` when no `if`/`ifversion` exists at all. + if (!/\{%-?\s*(?:if|ifversion)\s/.test(content)) { + content = content.replace(/\{%-?\s*else\s*-?%\}/g, '') + content = content.replace(/\{%-?\s*elsif\s+[^%]*?-?%\}/g, '') + } + return content } diff --git a/src/languages/tests/correct-translation-content.ts b/src/languages/tests/correct-translation-content.ts index 26f5382ff528..fb657cea1273 100644 --- a/src/languages/tests/correct-translation-content.ts +++ b/src/languages/tests/correct-translation-content.ts @@ -7,6 +7,7 @@ function fix(content: string, code: string, englishContent = '') { return correctTranslatedContentStrings(content, englishContent, { code, relativePath: 'test.md', + skipOrphanStripping: true, }) } @@ -781,11 +782,13 @@ describe('correctTranslatedContentStrings', () => { test('removes orphaned endif when no matching ifversion/elsif opener exists', () => { // Caused by translations where only the closing tag survived (e.g. user-api.md reusable) - expect(fix('Some content\n{% endif %}\nMore content', 'fr')).toBe( + const fixWithStrip = (s: string) => + correctTranslatedContentStrings(s, '', { code: 'fr', relativePath: 'test.md' }) + expect(fixWithStrip('Some content\n{% endif %}\nMore content')).toBe( 'Some content\n\nMore content', ) - expect(fix('Line one\n{%- endif %}\nLine two', 'fr')).toBe('Line one\n\nLine two') - expect(fix('Text {%- endif -%} more', 'fr')).toBe('Text more') + expect(fixWithStrip('Line one\n{%- endif %}\nLine two')).toBe('Line one\n\nLine two') + expect(fixWithStrip('Text {%- endif -%} more')).toBe('Text more') }) test('preserves endif when matching ifversion opener is present', () => { @@ -1464,6 +1467,33 @@ describe('correctTranslatedContentStrings', () => { expect(fix('{{%raw %}', 'es')).toBe('{% raw %}') expect(fix('{{% raw %}', 'es')).toBe('{% raw %}') }) + + test('rejoins broken bullet markers split across lines (all languages)', () => { + // Lone `*` with content on indented next line → `* content` + const broken = '* \n [AUTOTITLE](/orgs/transfer)' + const expected = '* [AUTOTITLE](/orgs/transfer)' + for (const lang of ['ja', 'de', 'es', 'fr', 'ko', 'pt', 'ru', 'zh']) { + expect(fix(broken, lang)).toBe(expected) + } + // No trailing space variant + expect(fix('*\n [AUTOTITLE](/path)', 'ko')).toBe('* [AUTOTITLE](/path)') + // Multiple consecutive broken bullets + expect(fix('* \n one\n* \n two', 'fr')).toBe('* one\n* two') + // Valid bullets are not modified + expect(fix('* normal\n* another', 'de')).toBe('* normal\n* another') + }) + + test('rejoins broken table cells split across lines (all languages)', () => { + const broken = '|\n **クラウドとサーバー** | 説明' + const expected = '| **クラウドとサーバー** | 説明' + for (const lang of ['ja', 'de', 'es', 'fr', 'ko', 'pt', 'ru', 'zh']) { + expect(fix(broken, lang)).toBe(expected) + } + // Pipe with trailing whitespace + expect(fix('| \n cell text', 'zh')).toBe('| cell text') + // Valid table rows are not modified + expect(fix('| a | b |\n| c | d |', 'es')).toBe('| a | b |\n| c | d |') + }) }) // ─── EDGE CASES ────────────────────────────────────────────────────