Skip to content

Commit b5f08dd

Browse files
authored
Merge pull request #46107 from github/repo-sync
Repo sync
2 parents 87a7556 + 03c1c34 commit b5f08dd

371 files changed

Lines changed: 3348 additions & 5599 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: Check for non-English titles
2+
3+
# **What it does**: Closes issues/PRs whose title contains non-Latin-script
4+
# characters, which is a common pattern for spam/scam submissions (e.g.
5+
# fake certificate/badge issues written in Arabic, Cyrillic, CJK, etc.)
6+
# **Why we have it**: We get spam in the open-source repo with titles in
7+
# scripts our triage team can't read, making it hard to evaluate intent.
8+
# **Who does it impact**: Open-source contributors.
9+
10+
on:
11+
issues:
12+
types: [opened]
13+
pull_request_target:
14+
types: [opened]
15+
16+
permissions:
17+
contents: read
18+
issues: write
19+
pull-requests: write
20+
21+
jobs:
22+
non-english-title-check:
23+
name: Flag and close non-English titles
24+
if: github.repository == 'github/docs'
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
28+
with:
29+
github-token: ${{ secrets.DOCS_BOT_PAT_BASE }}
30+
script: |
31+
const isIssue = !!context.payload.issue
32+
const item = context.payload.issue || context.payload.pull_request
33+
const owner = 'github'
34+
const repo = 'docs'
35+
const title = item.title || ''
36+
37+
// Allow: basic Latin letters/digits/punctuation, common symbols,
38+
// and emoji (surrogate pairs), so normal English titles pass.
39+
// Flag titles containing letters from non-Latin scripts
40+
// (Arabic, CJK, Cyrillic, Devanagari, Hebrew, Thai, etc.)
41+
const nonLatinScriptRegex = /(?!\p{Script=Latin})\p{Letter}/u
42+
43+
if (!nonLatinScriptRegex.test(title)) {
44+
return
45+
}
46+
47+
try {
48+
await github.rest.teams.getMembershipForUserInOrg({
49+
org: 'github',
50+
team_slug: 'employees',
51+
username: context.payload.sender.login,
52+
})
53+
// Don't action GitHub employees
54+
return
55+
} catch (err) {
56+
// Not an employee — continue
57+
}
58+
59+
const commentBody =
60+
`Thanks for your contribution! It looks like the title of this ${isIssue ? 'issue' : 'pull request'} contains non-English characters. ` +
61+
`To help our team triage effectively, please resubmit with an English-language title describing the change or problem. ` +
62+
`I'm closing this for now, but feel free to open a new one!`
63+
64+
if (isIssue) {
65+
await github.rest.issues.update({
66+
owner, repo,
67+
issue_number: item.number,
68+
labels: ['invalid'],
69+
state: 'closed',
70+
})
71+
await github.rest.issues.createComment({
72+
owner, repo,
73+
issue_number: item.number,
74+
body: commentBody,
75+
})
76+
} else {
77+
await github.rest.pulls.update({
78+
owner, repo,
79+
pull_number: item.number,
80+
state: 'closed',
81+
})
82+
await github.rest.issues.createComment({
83+
owner, repo,
84+
issue_number: item.number,
85+
body: commentBody,
86+
})
87+
}

‎.github/workflows/check-for-spammy-prs.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ name: Check for Spammy PRs
66

77
on:
88
pull_request_target:
9-
types: [opened]
9+
types: [opened, edited, reopened, synchronize]
1010

1111
permissions:
1212
contents: read

‎.github/workflows/triage-unallowed-contributions.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
github.repository == 'github/docs' &&
2020
github.event.pull_request.user.login != 'docs-bot' &&
2121
github.event.pull_request.user.login != 'dependabot[bot]'
22+
&& !contains(github.event.pull_request.labels.*.name, 'skip-unallowed-check')
2223
}}
2324
runs-on: ubuntu-latest
2425
steps:

‎content/copilot/reference/copilot-cli-reference/cli-command-reference.md‎

Lines changed: 51 additions & 9 deletions
Large diffs are not rendered by default.

‎content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md‎

Lines changed: 12 additions & 4 deletions
Large diffs are not rendered by default.

‎content/copilot/reference/copilot-cli-reference/cli-plugin-reference.md‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ You can use the following commands in the terminal to manage plugins for {% data
2828
| `copilot plugin uninstall NAME` (aliases `remove`, `rm`) | Remove a plugin |
2929
| `copilot plugin list` | List installed plugins |
3030
| `copilot plugin update NAME` | Update a named plugin. Use `--all` to update all installed plugins at once. |
31-
| `copilot plugin enable NAME` | Enable a previously disabled plugin |
32-
| `copilot plugin disable NAME` | Disable a plugin without uninstalling it |
31+
| `copilot plugin enable NAME` | Enable a previously disabled plugin. The change persists to configuration and applies to future sessions. This works for marketplace installs and direct installs (from `owner/repo`, a URL, or a local path) alike. |
32+
| `copilot plugin disable NAME` | Disable a plugin without uninstalling it. A `--plugin-dir` mount stays read-only since it has no persisted activation to change. |
3333
| `copilot plugin marketplace add SPECIFICATION` | Register a marketplace. The marketplace's own name, from its `marketplace.json` manifest, becomes its registration key—there is no option to set a custom local name. |
3434
| `copilot plugin marketplace list` | List registered marketplaces |
3535
| `copilot plugin marketplace browse NAME` | Browse marketplace plugins |
@@ -111,9 +111,9 @@ In interactive mode, run `/plugin marketplace update [NAME]` (alias `/plugin mar
111111

112112
## `plugin.json`
113113

114-
All plugins consist of a plugin directory containing a manifest file named `plugin.json`. Agent Plugins 1.0 requires the manifest at the plugin root. Legacy plugins support the alternative locations listed in [File locations](#file-locations). See [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/plugins-creating).
114+
All plugins consist of a plugin directory containing a manifest file named `plugin.json`. Agent Plugins requires the manifest at the plugin root. A root `plugin.json` that targets Agent Plugins takes precedence over `.plugin/plugin.json` and `.claude-plugin/plugin.json` per spec §5.1. Legacy plugins support the alternative locations listed in [File locations](#file-locations). See [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/plugins-creating).
115115

116-
{% data variables.copilot.copilot_cli_short %} supports both the legacy plugin manifest and the Agent Plugins 1.0 manifest. The exact `$schema` value `https://agent-plugins.org/schemas/1.0.0/plugin.schema.json` opts a plugin into Agent Plugins 1.0 semantics. A manifest without this value uses the legacy format and loads as before.
116+
{% data variables.copilot.copilot_cli_short %} supports both the legacy plugin manifest and the Agent Plugins manifest. {% data variables.copilot.copilot_cli_short %} recognizes the canonical `$schema` values for Agent Plugins (Open Plugin Spec) v1.0.0 (`https://agent-plugins.org/schemas/1.0.0/plugin.schema.json`) and v1.1.0 (`https://agent-plugins.org/schemas/1.1.0/plugin.schema.json`), opting a plugin into Agent Plugins semantics. A manifest without one of these exact values uses the legacy format and loads as before. If a plugin declares an Agent Plugins version that {% data variables.copilot.copilot_cli_short %} doesn't support, the CLI rejects the plugin instead of silently falling back to legacy mode. A rejected plugin contributes no hooks, LSP servers, MCP servers, skills, commands, agents, rules, or extension directories.
117117

118118
### Agent Plugins 1.0 manifest fields
119119

@@ -123,7 +123,7 @@ The following fields are allowed:
123123

124124
| Field | Type | Required | Description |
125125
|---------------|----------|----------|-------------|
126-
| `$schema` | string | Yes | Must be `https://agent-plugins.org/schemas/1.0.0/plugin.schema.json`. |
126+
| `$schema` | string | Yes | Must be a recognized Agent Plugins `$schema` URL (v1.0.0 or v1.1.0). Unsupported Agent Plugins versions are rejected. |
127127
| `name` | string | Yes | Plugin name. See [Name constraints](#name-constraints). |
128128
| `version` | string | No | Version string. Semantic Versioning is recommended. |
129129
| `description` | string | No | Brief description. |
@@ -152,9 +152,9 @@ Agent Plugins 1.0 defines two portable component types:
152152
* Skills in immediate subdirectories of `skills/` that contain a `SKILL.md` file.
153153
* MCP servers in `mcp.json` at the plugin root.
154154

155-
These locations are fixed and cannot be configured in `plugin.json`. The root `mcp.json` must declare `https://agent-plugins.org/schemas/1.0.0/mcp.schema.json` in its `$schema` field. The CLI accepts `stdio`, `streamable-http`, and `sse` MCP transport names.
155+
These locations are fixed and cannot be configured in `plugin.json`. Skills load only from `skills/`—there is no root `SKILL.md` fallback (legacy plugins fall back to a root `SKILL.md` when no `skills/` directory exists). The root `mcp.json` must declare a recognized Agent Plugins `$schema` version (matching the same version as `plugin.json`) in its `$schema` field. The top-level envelope is closed, and each server entry is validated against its transport schema; invalid server entries are skipped individually while valid entries still load. The CLI accepts `stdio`, `streamable-http`, and `sse` MCP transport names.
156156

157-
For `stdio` servers, the CLI provides `PLUGIN_ROOT` and `PLUGIN_DATA` in the subprocess environment. It expands `${PLUGIN_ROOT}` and `${PLUGIN_DATA}` in the server's `args`, `env` values, and `cwd`. `PLUGIN_DATA` points to a persistent, writable directory for the installed plugin.
157+
For `stdio` servers, the CLI provides `PLUGIN_ROOT` and `PLUGIN_DATA` in the subprocess environment. It expands `${PLUGIN_ROOT}` and `${PLUGIN_DATA}` (plus the `CLAUDE_PLUGIN_DATA` and `COPILOT_PLUGIN_DATA` aliases) in the server's `args`, `env` values, and `cwd`. `PLUGIN_DATA` points to a persistent, writable directory for the installed plugin. Remote `http`, `sse`, and `streamable-http` server config values are passed through literally, with no placeholder or environment-variable expansion.
158158

159159
Agent Plugins 1.0 does not define portable agents, hooks, commands, rules, or LSP servers. These remain client-specific. Client-specific manifest data belongs in `extensions`, keyed by reverse-domain namespace. Client-specific files belong in a top-level directory with the same namespace. Clients ignore namespaces they do not support.
160160

@@ -367,12 +367,12 @@ Both the `github` and `url` source types accept an optional `sha` field to pin i
367367
|----------------------|------|
368368
| Installed plugins | `~/.copilot/installed-plugins/MARKETPLACE/PLUGIN-NAME` (installed via a marketplace) and `~/.copilot/installed-plugins/_direct/SOURCE-ID/` (installed directly) |
369369
| Marketplace cache | Platform cache directory: `~/.cache/copilot/marketplaces/` (Linux), `~/Library/Caches/copilot/marketplaces/` (macOS). Overridable with `COPILOT_CACHE_HOME`. |
370-
| Plugin manifest | Agent Plugins 1.0: `plugin.json` at the plugin root. Legacy plugins: `.plugin/plugin.json`, `plugin.json`, `.github/plugin/plugin.json`, or `.claude-plugin/plugin.json` (checked in this order). |
370+
| Plugin manifest | Agent Plugins (v1.0.0 or v1.1.0): `plugin.json` at the plugin root. A root manifest targeting Agent Plugins takes precedence over `.plugin/plugin.json` and `.claude-plugin/plugin.json` per spec §5.1. Legacy plugins: `.plugin/plugin.json`, `plugin.json`, `.github/plugin/plugin.json`, or `.claude-plugin/plugin.json` (checked in this order). |
371371
| Marketplace manifest | `marketplace.json`, `.plugin/marketplace.json`, `.github/plugin/marketplace.json`, or `.claude-plugin/marketplace.json` (checked in this order) |
372372
| Agents | Legacy plugins: `agents/` (default, overridable in manifest). |
373-
| Skills | Agent Plugins 1.0: `skills/` (fixed). Legacy plugins: `skills/` (default, overridable in manifest). |
373+
| Skills | Agent Plugins: `skills/` (fixed, no root `SKILL.md` fallback). Legacy plugins: `skills/` (default, overridable in manifest), falling back to a root `SKILL.md` when no `skills/` directory exists. |
374374
| Hooks configuration | Legacy plugins: `hooks.json` or `hooks/hooks.json`. |
375-
| MCP configuration | Agent Plugins 1.0: `mcp.json`. Legacy plugins: `.mcp.json`, `.github/mcp.json`, or the `mcpServers` manifest field. |
375+
| MCP configuration | Agent Plugins: `mcp.json`. Legacy plugins: `.mcp.json`, `.github/mcp.json`, or the `mcpServers` manifest field. |
376376
| LSP configuration | Legacy plugins: `lsp.json` or `.github/lsp.json`. |
377377
| Plugin data | For Agent Plugins 1.0 MCP servers, `${PLUGIN_DATA}` (also available as `${COPILOT_PLUGIN_DATA}` and `${CLAUDE_PLUGIN_DATA}`) points to a persistent, writable directory unique to each installed plugin. Use this for plugin-specific runtime data instead of paths inside the installed-plugins cache directory. |
378378

‎content/copilot/reference/hooks-reference.md‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,18 @@ Each hook event delivers a JSON payload to the hook handler. Two payload formats
290290
}
291291
```
292292

293+
**Output:**
294+
295+
```typescript
296+
{
297+
additionalContext?: string;
298+
}
299+
```
300+
301+
Only `additionalContext` is consumed for `sessionStart` (command and HTTP variants). Return `{}` or empty for no action.
302+
303+
When multiple `sessionStart` hooks run, successful hooks that return a non-empty string `additionalContext` contribute in execution order, separated by exactly `"\n\n"`. An empty or whitespace-only string does not erase already-accumulated context; if every hook returns only empty or whitespace-only strings, the last one is kept. The combined string (including separators) is bounded by the same 10 MiB hook-output limit—a contribution that would cross it is dropped, the previously accumulated context is kept, and a size-only warning is logged and raised in the session.
304+
293305
### `sessionEnd` / `SessionEnd`
294306

295307
> [!NOTE]
@@ -551,6 +563,20 @@ Tools with no Claude equivalent keep their runtime names.
551563
}
552564
```
553565

566+
**Output:**
567+
568+
```typescript
569+
{
570+
additionalContext?: string;
571+
}
572+
```
573+
574+
If `additionalContext` is returned, it is prepended to the subagent's first user message, giving hooks a way to inject project-specific context, policies, or instructions into every subagent invocation.
575+
576+
When multiple `subagentStart` hooks run, they accumulate the same way as `sessionStart`: successful hooks with a non-empty string `additionalContext` contribute in execution order joined by `"\n\n"`, empty or whitespace-only strings don't erase already-accumulated context, and the combined string is bounded by the 10 MiB hook-output limit (an over-limit contribution is dropped, the prior context is kept, and a size-only warning is logged and raised in the session).
577+
578+
**Matcher:** Supports an optional `matcher` field that filters by agent name. The value is treated as a regular expression wrapped as `^(?:matcher)$` and tested against `agentName`. The pattern must match the **entire** agent name, not just a substring. If the pattern is not a valid regular expression, the hook is skipped entirely (it will not fire for any agent).
579+
554580
### `subagentStop` / `SubagentStop`
555581

556582
Fires when a subagent completes normally, before returning results to the parent. `stopReason` is currently always `"end_turn"`. This hook fires before large-response spill handling, so `response` (or `last_assistant_message` in the {% data variables.product.prodname_vscode_shortname %} compatible format) carries the full final subagent response text.

‎src/article-api/lib/get-all-toc-items.ts‎

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,12 @@ interface TocItem extends LinkData {
1515
childTocItems?: TocItem[]
1616
}
1717

18-
/**
19-
* Recursively gathers all TOC items from a page and its descendants.
20-
* This mirrors the behavior of getTocItems() in the generic-toc middleware
21-
* but works with the page.children frontmatter property.
22-
*/
18+
// Mirrors getTocItems() in src/frame/middleware/context/generic-toc.ts for frontmatter children.
2319
export async function getAllTocItems(
2420
page: Page,
2521
context: Context,
2622
options: {
27-
/** Only recurse into children whose resolved path starts with this prefix.
28-
* Prevents cross-product traversal (e.g. /en/rest listing /enterprise-admin). */
23+
// Prevents cross-product traversal, such as /en/rest listing /enterprise-admin.
2924
basePath?: string
3025
} = {},
3126
): Promise<TocItem[]> {
@@ -41,8 +36,7 @@ export async function getAllTocItems(
4136
)
4237
const pathname = pagePermalink ? pagePermalink.href : `/${languageCode}`
4338

44-
// On the first call, set basePath to this page's path so recursion
45-
// stays within the same product section.
39+
// Keeps recursive children within the first page's product section.
4640
const basePath = options.basePath ?? pathname
4741

4842
const resolvedChildren = pageWithChildren.children
@@ -69,7 +63,6 @@ export async function getAllTocItems(
6963

7064
const category = childPage.category || []
7165

72-
// Only recurse if the child is within the same product section
7366
const withinSection = href.startsWith(basePath)
7467
const childTocItems =
7568
withinSection && childPage.children && childPage.children.length > 0
@@ -83,14 +76,10 @@ export async function getAllTocItems(
8376
return items
8477
}
8578

86-
/**
87-
* Flattens nested TOC items into a single array.
88-
* Only includes leaf nodes (items without children) or all items based on options.
89-
*/
9079
export function flattenTocItems(
9180
tocItems: TocItem[],
9281
options: {
93-
excludeParents?: boolean // If true, only include items without children
82+
excludeParents?: boolean
9483
} = {},
9584
): LinkData[] {
9685
const { excludeParents = true } = options
@@ -101,9 +90,7 @@ export function flattenTocItems(
10190
for (const item of items) {
10291
const hasChildren = item.childTocItems && item.childTocItems.length > 0
10392

104-
// Include this item if it's a leaf or if we're including parents
105-
// Deduplicate by href - needed when a page lists both individual
106-
// articles and their parent group as children (e.g., bespoke landing pages)
93+
// Bespoke landing pages can list both articles and their parent group.
10794
if (!hasChildren || !excludeParents) {
10895
if (!seen.has(item.href)) {
10996
seen.add(item.href)
@@ -125,13 +112,7 @@ export function flattenTocItems(
125112
return result
126113
}
127114

128-
/**
129-
* Check whether a string contains markdown link syntax that would need
130-
* processing by the unified pipeline (e.g. link rewriting, AUTOTITLE).
131-
*
132-
* Use this to short-circuit expensive rendering when the text is
133-
* Liquid-only and contains no markdown that needs transformation.
134-
*/
115+
// Liquid-only properties can skip the full unified pipeline.
135116
function hasMarkdownLinks(text: string): boolean {
136117
return text.includes('[') && text.includes('](/')
137118
}
@@ -141,11 +122,7 @@ const RAW_PROP_MAP = {
141122
intro: 'rawIntro',
142123
} as const
143124

144-
/**
145-
* Fast-path rendering for page properties. Renders Liquid only, skipping
146-
* the full unified pipeline. Falls back to page.renderProp() when the
147-
* Liquid output contains markdown links that need rewriting.
148-
*/
125+
// Falls back to page.renderProp() when Liquid output still has markdown links.
149126
async function renderPropFast(
150127
page: PageWithChildren,
151128
prop: keyof typeof RAW_PROP_MAP,

‎src/article-api/lib/get-link-data.ts‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
import type { Context, Page } from '@/types'
22
import type { LinkData } from '@/article-api/transformers/types'
33

4-
/**
5-
* Resolves link data (title, href, intro) for a given href and page
6-
*
7-
* This helper is used by landing page transformers to build link lists.
8-
* It resolves the page from an href (relative or absolute), renders its title
9-
* and intro, and
10-
* returns the canonical permalink.
11-
*/
124
export async function getLinkData(
135
href: string,
146
languageCode: string,

‎src/article-api/lib/graphql-helpers.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { Context, Page } from '@/types'
22
import { renderContent } from '@/content-render/index'
33
import matter from '@gr2m/gray-matter'
44

5-
// Returns the part of the page markdown before the auto-generated marker.
65
export async function extractManualContent(page: Page, context: Context): Promise<string> {
76
if (!page.markdown) return ''
87

0 commit comments

Comments
 (0)