Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Docs changelog

**2 April 2026**

We've expanded the documentation for custom agents in Copilot CLI, adding information about the built-in agents.

[About custom agents](https://docs.github.com/en/copilot/concepts/agents/copilot-cli/about-custom-agents#built-in-agents)

<hr>

**27 March 2026**

We've introduced a new discovery landing page design for all the top-level doc sets on docs.github.com. The landing pages highlight recommended articles and give users the ability to filter articles by category with a drop down menu. Every article across the site now includes category metadata, making it easier to browse doc sets without relying solely on search. This replaces the previous product-landing layout across 35 doc sets.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ category:

This procedure demonstrates how to create a workflow template and metadata file. The metadata file describes how the workflow templates will be presented to users when they are creating a new workflow.

1. If it doesn't already exist, create a new repository named `.github` in your organization.
1. If it doesn't already exist, create a new {% ifversion actions-nga %} {% else %}public {% endif %}repository named `.github` in your organization.
1. Create a directory named `workflow-templates`.
1. Create your new workflow file inside the `workflow-templates` directory.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ When a reusable workflow is triggered by a caller workflow, the `github` context

Reference information to use when creating workflow templates for your organization.

{% ifversion actions-nga %}
### Workflow template availability

You can use templates in repositories that match or have more restricted visibility than the template repository.
Expand All @@ -132,6 +133,7 @@ Because public workflow templates require a public `.github` repository, they ar
### Granting access for private/internal repositories

If you're using a private or internal `.github` repository, you need to grant Read access to users or teams who should be able to use the templates.
{% endif %}

### The `$default-branch` placeholder

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,6 @@ Some features on {% data variables.enterprise.data_residency_site %} are current

### {% data variables.product.prodname_github_codespaces %}

{% data variables.product.prodname_github_codespaces %} on {% data variables.enterprise.data_residency_site %} are in {% data variables.release-phases.public_preview %} and are available in all {% data variables.enterprise.data_residency %} regions: EU, Australia, US, and Japan.
{% data variables.product.prodname_github_codespaces %} on {% data variables.enterprise.data_residency_site %} are in {% data variables.release-phases.public_preview %} and are available in all {% data variables.enterprise.data_residency %} regions.

To use {% data variables.product.prodname_github_codespaces %} from {% data variables.product.prodname_vscode_shortname %} desktop with an enterprise on {% data variables.enterprise.data_residency_site %}, you must configure the `Github-enterprise: Uri` and `Github > Codespaces: Auth Provider` settings. For more information, see [AUTOTITLE](/codespaces/developing-in-a-codespace/using-github-codespaces-in-visual-studio-code#connecting-to-an-enterprise-on-ghecom).
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ gh api \
{% data reusables.copilot.optional-select-custom-agent-generic %}
{% data reusables.copilot.optional-select-copilot-coding-agent-model %}
1. Optionally, provide additional instructions. These will be passed to {% data variables.product.prodname_copilot_short %} alongside your issue contents.
1. Press <kbd>Command</kbd>+<kbd>Enter</kbd> to assign the issue.
1. Press <kbd>Command</kbd>+<kbd>Enter</kbd> (macOS) or <kbd>Ctrl</kbd>+<kbd>Enter</kbd> (Windows) to assign the issue.

{% data variables.product.prodname_copilot_short %} will start a new session. {% data variables.product.prodname_copilot_short %} will work on the task and push changes to its pull request, then add you as a reviewer when it has finished, triggering a notification.

Expand Down Expand Up @@ -613,7 +613,7 @@ To see all of the available options, run `gh agent-task create --help`.
1. Optionally, select a base branch for {% data variables.product.prodname_copilot_short %}'s pull request. {% data variables.product.prodname_copilot_short %} will create a new branch based on this branch, then push the changes to a pull request targeting that branch.
{% data reusables.copilot.optional-select-custom-agent-generic %}
{% data reusables.copilot.optional-select-copilot-coding-agent-model %}
1. Press <kbd>Command</kbd>+<kbd>Enter</kbd> to start the task.
1. Press <kbd>Command</kbd>+<kbd>Enter</kbd> (macOS) or <kbd>Ctrl</kbd>+<kbd>Enter</kbd> (Windows) to start the task.

{% data variables.product.prodname_copilot_short %} will start a new session. {% data variables.product.prodname_copilot_short %} will work on the task and push changes to its pull request, then add you as a reviewer when it has finished, triggering a notification.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ To see all of the available options, run `gh agent-task list --help` or `gh agen
{% data reusables.copilot.coding-agent.raycast-setup %}
1. Open Raycast, search for "{% data variables.product.prodname_copilot_short %}," find the **View Tasks** command, then press <kbd>Enter</kbd>.
1. Click **Sign in with {% data variables.product.github %}**, then complete the authentication flow. Raycast will re-open.
1. You'll see a list of your tasks. To navigate to the linked pull request, press <kbd>Enter</kbd>. To view the session logs, press <kbd>Command</kbd>+<kbd>L</kbd>.
1. You'll see a list of your tasks. Select a task, then use the following keyboard shortcuts:
* To watch the session logs live, press <kbd>Enter</kbd>. The logs update in real time, so you can monitor {% data variables.product.prodname_copilot_short %}'s progress without leaving Raycast.
* To open the session logs in the browser, press <kbd>Command</kbd>+<kbd>Enter</kbd> (macOS) or <kbd>Ctrl</kbd>+<kbd>Enter</kbd> (Windows).
* To open the linked pull request, press <kbd>Command</kbd>+<kbd>P</kbd> (macOS) or <kbd>Ctrl</kbd>+<kbd>P</kbd> (Windows).

> [!NOTE]
> If you are unable to see some tasks in Raycast, the organization that owns the repository may have enabled {% data variables.product.prodname_oauth_app %} access restrictions. To learn how to request approval for the "{% data variables.product.prodname_copilot %} for Raycast" {% data variables.product.prodname_oauth_app %}, see [AUTOTITLE](/account-and-profile/how-tos/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/requesting-organization-approval-for-oauth-apps).
Expand Down Expand Up @@ -131,7 +134,7 @@ Commits from {% data variables.copilot.copilot_coding_agent %} have the followin

## Using the session logs to understand {% data variables.product.prodname_copilot_short %}'s approach

You can dive into {% data variables.product.prodname_copilot_short %}'s session logs in {% data variables.product.github %} or {% data variables.product.prodname_vscode %} to understand how it approached your task.
You can dive into {% data variables.product.prodname_copilot_short %}'s session logs in {% data variables.product.github %}, {% data variables.product.prodname_vscode %}, or Raycast to understand how it approached your task.

In the session logs, you can see {% data variables.product.prodname_copilot_short %}'s internal monologue and the tools it used to understand your repository, make changes and validate its work.

Expand Down
5 changes: 5 additions & 0 deletions data/features/actions-nga.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# New feature: actions-nga
# Versioning for Actions NGA (Next Generation Architecture) features
versions:
fpt: '*'
ghec: '*'
2 changes: 1 addition & 1 deletion data/reusables/copilot/coding-agent/raycast-intro.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[Raycast](https://www.raycast.com/) is an extensible launcher for Windows and macOS. With the {% data variables.product.prodname_copilot %} extension for Raycast, you can start and track {% data variables.copilot.copilot_coding_agent %} tasks wherever you are on your computer.
[Raycast](https://www.raycast.com/) is an extensible launcher for Windows and macOS. With the {% data variables.product.prodname_copilot %} extension for Raycast, you can start and track {% data variables.copilot.copilot_coding_agent %} tasks and watch session logs live wherever you are on your computer.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Data residency makes it easy to separate open source and enterprise work, and he

The available regions are:

* EU
* EU (includes EFTA countries, Norway and Switzerland, as of May 1, 2026)
* Australia
* US
* Japan
Expand Down
11 changes: 0 additions & 11 deletions eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,9 @@ export default [
'src/article-api/transformers/audit-logs-transformer.ts',
'src/article-api/transformers/rest-transformer.ts',
'src/codeql-cli/scripts/convert-markdown-for-docs.ts',
'src/content-linter/lib/helpers/get-lintable-yml.ts',
'src/content-linter/lib/helpers/print-annotations.ts',
'src/content-linter/lib/helpers/utils.ts',
'src/content-linter/lib/init-test.ts',
'src/content-linter/lib/linting-rules/code-annotations.ts',
'src/content-linter/lib/linting-rules/index.ts',
'src/content-linter/lib/linting-rules/internal-links-no-lang.ts',
'src/content-linter/lib/linting-rules/journey-tracks-liquid.ts',
'src/content-linter/lib/linting-rules/liquid-ifversion-versions.ts',
'src/content-linter/lib/linting-rules/liquid-versioning.ts',
Expand Down Expand Up @@ -253,8 +249,6 @@ export default [
'src/frame/lib/page-data.ts',
'src/frame/lib/page.ts',
'src/frame/lib/read-frontmatter.ts',
'src/frame/middleware/find-page.ts',
'src/frame/middleware/resolve-carousels.ts',
'src/frame/tests/page.ts',
'src/frame/tests/read-frontmatter.ts',
'src/frame/tests/server.ts',
Expand All @@ -278,7 +272,6 @@ export default [
'src/links/lib/update-internal-links.ts',
'src/links/scripts/check-github-github-links.ts',
'src/links/scripts/update-internal-links.ts',
'src/pages/_error.tsx',
'src/redirects/middleware/handle-redirects.ts',
'src/rest/components/get-rest-code-samples.ts',
'src/rest/lib/index.ts',
Expand All @@ -302,8 +295,6 @@ export default [
'src/search/lib/get-elasticsearch-results/general-search.ts',
'src/search/lib/routes/combined-search-route.ts',
'src/search/lib/search-request-params/get-search-from-request-params.ts',
'src/search/lib/search-request-params/search-params-objects.ts',
'src/search/lib/search-request-params/types.ts',
'src/search/middleware/search-routes.ts',
'src/search/scripts/index/index-cli.ts',
'src/search/scripts/index/utils/indexing-elasticsearch-utils.ts',
Expand All @@ -318,9 +309,7 @@ export default [
'src/types/markdownlint-rule-search-replace.d.ts',
'src/types/primer__octicons.d.ts',
'src/versions/scripts/use-short-versions.ts',
'src/webhooks/lib/index.ts',
'src/workflows/projects.ts',
'src/workflows/tests/actions-workflows.ts',
],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
Expand Down
6 changes: 5 additions & 1 deletion src/article-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ The `/api/article` endpoints return information about a page by `pathname`.

`api/article/meta` is highly cached, in JSON format.

### Autogenerated Content Transformers
### Redirects and the pagelist

The pagelist (`/api/pagelist/:lang/:version`) returns only **canonical permalinks**. The article API (`/api/article`, `/api/article/body`, `/api/article/meta`) transparently follows redirects—so URLs that don't appear in the pagelist (such as `redirect_from` aliases or old paths) may still return content.

When the article API resolves a redirect through the redirect table, the JSON response includes a `redirectedFrom` field containing the normalized pathname that was looked up (after trailing-slash removal and other standard normalization, not the raw originally-requested pathname). This field is only set for redirect-table lookups; it is not set for the bare `/` to `/<lang>` language rewrite. This lets consumers detect that the URL they requested is not canonical. The `/api/article/body` endpoint returns plain text, so `redirectedFrom` is not included in its response.

For autogenerated pages (REST, GraphQL, webhooks, landing pages, audit logs, etc), the Article API uses specialized transformers to convert the rendered content into markdown format. These transformers are located in `src/article-api/transformers/` and use an extensible architecture.

Expand Down
7 changes: 5 additions & 2 deletions src/article-api/middleware/article-pageinfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export async function getMetadata(req: ExtendedRequestWithPageInfo) {
// /articles or '/en/enterprise-server@latest/foo/bar)
// So by the time we get here, the pathname should be one of the
// page's valid permalinks.
const { page, pathname, archived } = req.pageinfo
const { page, pathname, archived, redirectedFrom } = req.pageinfo
const documentType = page?.documentType ?? null

if (archived && archived.isArchived) {
Expand All @@ -157,5 +157,8 @@ export async function getMetadata(req: ExtendedRequestWithPageInfo) {
const fromCache = await getPageInfoFromCache(page, pathname)
const { cacheInfo, ...meta } = fromCache

return { meta: { ...meta, documentType }, cacheInfo }
return {
meta: { ...meta, documentType, ...(redirectedFrom && { redirectedFrom }) },
cacheInfo,
}
}
1 change: 1 addition & 0 deletions src/article-api/middleware/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export const pageValidationMiddleware = (
if (!req.pageinfo.archived.isArchived) {
const redirect = getRedirect(pathname, redirectsContext)
if (redirect) {
req.pageinfo.redirectedFrom = pathname
pathname = redirect
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/article-api/tests/pageinfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface PageMetadata {
title: string
intro: string
documentType: string | null
redirectedFrom?: string
}

interface ErrorResponse {
Expand Down Expand Up @@ -46,6 +47,8 @@ describe('pageinfo api', () => {
'Get started using HubGit to manage Git repositories and collaborate with others.',
)
expect(meta.documentType).toBe('category')
// Canonical URLs should not have redirectedFrom
expect(meta.redirectedFrom).toBeUndefined()
// Check that it can be cached at the CDN
expect(res.headers['set-cookie']).toBeUndefined()
expect(res.headers['cache-control']).toContain('public')
Expand Down Expand Up @@ -90,13 +93,15 @@ describe('pageinfo api', () => {
expect(res.statusCode).toBe(200)
const meta = JSON.parse(res.body) as PageMetadata
expect(meta.title).toBe('HubGit.com Fixture Documentation')
expect(meta.redirectedFrom).toBe('/en/olden-days')
}
// Trailing slashes are always removed
{
const res = await get(makeURL('/en/olden-days/'))
expect(res.statusCode).toBe(200)
const meta = JSON.parse(res.body) as PageMetadata
expect(meta.title).toBe('HubGit.com Fixture Documentation')
expect(meta.redirectedFrom).toBe('/en/olden-days')
}
// Short code for latest version
{
Expand Down
1 change: 1 addition & 0 deletions src/article-api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ export type ExtendedRequestWithPageInfo = ExtendedRequest & {
pathname: string
page: Page
archived?: ArchivedVersion
redirectedFrom?: string
}
}
10 changes: 7 additions & 3 deletions src/content-linter/lib/helpers/get-lintable-yml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,13 @@ ajv.addKeyword({
type: 'string',
// For docs on defining validate see
// https://ajv.js.org/keywords.html#define-keyword-with-validate-function
// Using any for validate function params because AJV's type definitions for custom keywords are complex
validate: (compiled: any, data: any, schema: any, parentInfo: any): boolean => {
mdDict.set(parentInfo.instancePath, data)
validate: (
_compiled: boolean,
data: string,
_schema: unknown,
parentInfo?: { instancePath: string },
): boolean => {
if (parentInfo) mdDict.set(parentInfo.instancePath, data)
return true
},
errors: false,
Expand Down
21 changes: 14 additions & 7 deletions src/content-linter/lib/helpers/print-annotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@
*
*/

interface LintFlaw {
ruleNames: string[]
severity: string
lineNumber?: number
ruleDescription?: string
errorDetail?: string
context?: string
[key: string]: unknown
}

export function printAnnotationResults(
// Using 'any' type as results structure is dynamic and comes from various linting tools with different formats
results: any,
results: Record<string, LintFlaw[]>,
{
skippableRules = [],
skippableFlawProperties = [],
}: { skippableRules?: string[]; skippableFlawProperties?: string[] } = {},
) {
for (const [file, flaws] of Object.entries(results)) {
// Using 'any' type for flaws as they have varying structures depending on the linting rule
for (const flaw of flaws as any) {
for (const flaw of flaws) {
if (intersection(flaw.ruleNames, skippableRules)) {
continue
}
Expand Down Expand Up @@ -57,7 +65,6 @@ export function printAnnotationResults(
}
}

// Using 'any' types for generic array intersection utility function
function intersection(arr1: any[], arr2: any[]) {
return arr1.some((item: any) => arr2.includes(item))
function intersection(arr1: string[], arr2: string[]) {
return arr1.some((item) => arr2.includes(item))
}
10 changes: 3 additions & 7 deletions src/content-linter/lib/helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,14 @@ export function addFixErrorDetail(
addError(onError, lineNumber, `Expected: ${expected}`, ` Actual: ${actual}`, range, fixInfo)
}

export function forEachInlineChild(
export function forEachInlineChild<T = MarkdownToken>(
params: RuleParams,
type: string,
// Handler uses `any` for function parameter variance reasons. TypeScript's contravariance rules for function
// parameters mean that a function accepting a specific type cannot be assigned to a parameter of type `unknown`.
// Therefore, `unknown` cannot be used here, as different linting rules pass tokens with varying structures
// beyond the base MarkdownToken interface, and some handlers are async.
handler: (child: any, token?: any) => void | Promise<void>,
handler: (child: T, token?: MarkdownToken) => void | Promise<void>,
): void {
filterTokens(params, 'inline', (token: MarkdownToken) => {
for (const child of token.children!.filter((c) => c.type === type)) {
handler(child, token)
handler(child as unknown as T, token)
}
})
}
Expand Down
11 changes: 5 additions & 6 deletions src/content-linter/lib/linting-rules/internal-links-no-lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ import { filterTokens } from 'markdownlint-rule-helpers'

import { addFixErrorDetail, getRange } from '../helpers/utils'
import { languageKeys } from '@/languages/lib/languages'
import type { RuleParams, RuleErrorCallback, Rule } from '../../types'
import type { RuleParams, RuleErrorCallback, Rule, MarkdownToken } from '../../types'

export const internalLinksNoLang: Rule = {
names: ['GHD002', 'internal-links-no-lang'],
description: 'Internal links must not have a hardcoded language code',
tags: ['links', 'url'],
parser: 'markdownit',
function: (params: RuleParams, onError: RuleErrorCallback) => {
// Using 'any' type for token as markdownlint-rule-helpers doesn't provide TypeScript types
filterTokens(params, 'inline', (token: any) => {
for (const child of token.children) {
filterTokens(params, 'inline', (token: MarkdownToken) => {
for (const child of token.children!) {
if (child.type !== 'link_open') continue

// Example child.attrs:
Expand All @@ -21,8 +20,8 @@ export const internalLinksNoLang: Rule = {
// ['rel', 'canonical'],
// ]
// Attribute arrays are tuples of [attributeName, attributeValue] from markdownit parser
const hrefsMissingSlashes = child.attrs
// The attribute could also be `target` or `rel`
const hrefsMissingSlashes = child
.attrs! // The attribute could also be `target` or `rel`
.filter((attr: [string, string]) => attr[0] === 'href')
.filter((attr: [string, string]) => attr[1].startsWith('/') || !attr[1].startsWith('//'))
// Filter out link paths that start with language code
Expand Down
Loading
Loading