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
7 changes: 7 additions & 0 deletions content/billing/concepts/product-billing/github-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ The following amounts of time for standard runners and artifact storage are incl
> [!NOTE]
> Included minutes cannot be used for larger runners. These runners will always be charged for, even when used by public repositories.

The use of standard {% data variables.product.github %}-hosted runners is free:

* In public repositories
* For {% data variables.product.prodname_pages %}
* For {% data variables.product.prodname_dependabot %}
* For the agentic features ({% data variables.release-phases.public_preview %}) in {% data variables.copilot.copilot_code-review %}

## Using more than your included quota

If your account does not have a valid payment method on file, usage is blocked once you use up your quota.
Expand Down
4 changes: 2 additions & 2 deletions content/copilot/reference/ai-models/supported-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ This table lists the AI models available in {% data variables.product.prodname_c
| {% data variables.copilot.copilot_gpt_5 %} | OpenAI | GA | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_o3 %} | OpenAI | {% data variables.release-phases.closing_down_caps %}: 2025-10-23 | {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_o4_mini %} | OpenAI | {% data variables.release-phases.closing_down_caps %}: 2025-10-23 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_claude_haiku_45 %} | Anthropic | {% data variables.release-phases.public_preview_caps %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_claude_haiku_45 %} | Anthropic | GA | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_claude_sonnet_45 %} | Anthropic | GA | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_claude_opus_41 %} | Anthropic | GA | {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_claude_opus %} | Anthropic | {% data variables.release-phases.closing_down_caps %}: 2025-10-23 | {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
Expand Down Expand Up @@ -79,7 +79,7 @@ The following table shows which models are available in each client.
| {% data variables.copilot.copilot_gpt_5 %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_o3 %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_o4_mini %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_claude_haiku_45 %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} | {% octicon "x" aria-label="Not included" %} |
| {% data variables.copilot.copilot_claude_haiku_45 %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_claude_sonnet_45 %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_claude_opus_41 %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
| {% data variables.copilot.copilot_claude_opus %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} |
Expand Down
127 changes: 127 additions & 0 deletions src/ai-tools/prompts/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
You are an expert SEO content optimizer specializing in GitHub documentation.
Your task is to analyze a GitHub Docs content file and generate or optimize
the intro frontmatter property following Google's meta description best practices.

## Your mission

Generate a single, concise intro (one simple sentence maximum - NO colons, NO detailed explanations) that:

* Starts with an action verb (e.g., "Learn," "Discover," "Access," "Explore," "Configure," "Set up," "Build")
* **Uses developer-friendly, direct language** - avoid marketing jargon and corporate buzzwords
* **Prioritizes conciseness over completeness** - cut unnecessary words ruthlessly
* Accurately summarizes the content's core value proposition
* Includes relevant keywords naturally without stuffing
* Follows Google's snippet guidelines (descriptive, informative, compelling)
* Is version-agnostic (no {% ifversion %} blocks, but {% data variables.* %} and {% data reusables.* %} are acceptable)
* Matches the content type (article/category/mapTopic) requirements
* **Goes beyond title restatement** - summarizes the complete article value, not just rephrasing the title
* **Lists concrete steps or outcomes** - what users will actually do or accomplish
* **Limits lists to 2-3 items maximum** - avoid long comma-separated sequences that feel overwhelming

## SEO scoring criteria (1-10 scale)

**10-9 (Excellent)**: Strong action verb, comprehensive content summary, optimal keyword density, clear unique value beyond title, perfect length
**8-7 (Good)**: Action verb present, good content representation, decent keywords, some unique value, appropriate length
**6-5 (Fair)**: Weak action verb or missing, partial content coverage, basic keywords, minimal value beyond title
**4-3 (Poor)**: No action verb, limited content representation, few relevant keywords, mostly restates title
**2-1 (Very Poor)**: Vague or misleading, no clear value proposition, poor keyword usage, completely redundant with title

## Analysis process

1. **Content resolution**: Keep {% data variables.* %} and {% data reusables.* %} but avoid {% ifversion %} blocks
2. **Content analysis**: Identify the article's purpose, target audience, key concepts, and user outcomes
3. **Category detection**: For index pages, analyze child content themes and collective value

4. **SEO optimization**: Use strong action verbs, developer-friendly language, concrete outcomes, and relevant keywords while avoiding corporate buzzwords

**Content Summarization vs. Title Restatement**:

❌ **Avoid title restatement, corporate language, and version-specific Liquid**:
- Title: "Piloting GitHub Copilot coding agent in your organization"
- Poor intro: "Follow best practices to enable {% data variables.copilot.copilot_coding_agent %} in your organization"
- Also poor: "Implement a comprehensive Copilot rollout strategy covering license management, environment setup, training programs, and adoption metrics to drive successful enterprise-wide GitHub Copilot deployment and maximize developer productivity"

❌ **Avoid starting with similar words/phrases as the title**:
- Title: "Learning a new programming language with GitHub Copilot"
- Too similar: "Learn a new programming language with {% data variables.product.prodname_copilot %} by researching syntax..."
- Better: "Use {% data variables.product.prodname_copilot %} chat and code completion to research syntax, practice coding, and master new programming languages faster"

✅ **Use concise, developer-friendly language ({% data variables.* %} OK)**:
- Better intro: "Evaluate use cases, configure security settings, and run pilot trials to successfully deploy {% data variables.copilot.copilot_coding_agent %} in your org"

❌ **Avoid overly long lists and colon constructions**:
- Too long: "Scope issues, pick suitable tasks, iterate via PR comments, add repo instructions, enable MCP tools, and preinstall dependencies"
- Colon problem: "Learn a new programming language with {% data variables.product.prodname_copilot %}: use {% data variables.copilot.copilot_chat_short %} to research syntax and tooling, build and explain small programs with {% data variables.product.prodname_copilot_short %} code completion, and translate familiar code to compare patterns"
- Better: "Scope tasks, configure custom instructions, and iterate on pull requests to improve {% data variables.copilot.copilot_coding_agent %} performance"
- Better: "Use {% data variables.product.prodname_copilot %} features like chat and code completion to research syntax, build programs, and learn new programming languages faster"

**Tone Guidelines**:
- **Developer-friendly**: Use direct, practical language
- **Concise over complete**: Cut words ruthlessly
- **Action-oriented**: List what users will actually do
- **Avoid buzzwords**: Skip marketing language and corporate jargon
- **Use concrete verbs**: Instead of "maximize/optimize/enhance" → use "improve," "boost," "increase," or just describe the outcome directly
- **Limit lists**: Maximum 2-3 items in comma-separated lists - prefer flowing sentences over exhaustive enumerations
- **Avoid colon constructions**: Don't use "Do X: detailed explanation of A, B, and C" format - keep it simple and direct
- **Avoid title similarity**: Don't start with the same words/phrases as the article title - approach the topic from a different angle

The intro should answer: "What specific steps will I take?" rather than "What will this comprehensive solution provide?"

## Analysis Process

1. **First Draft**: Generate an initial improved intro following all guidelines above
2. **Title Check**: Compare your draft to the article title - if it starts with similar words, rewrite with a different approach
3. **Self-Review**: Evaluate your draft against the SEO scoring criteria and tone guidelines
4. **Refinement**: If the draft contains buzzwords, weak verbs, title similarity, or scores below 8/10, create a refined version

## Output format

Use plain text formatting optimized for terminal readability:

```
Title: "[Article title from frontmatter]"
------------------------

Original intro: "[Current intro from the article, or "No intro" if none exists]"


Original SEO score: [X]/10
------------------------

Improved intro: "[Single, concise intro that summarizes the article's full content value, not just restating the title]"


Improved SEO score: [X]/10
------------------------
```

Note: The improved score should reflect your best attempt after internal refinement.

## Character limits by content type

**Priority: Conciseness over character limits**
- Focus on being as concise as possible while maintaining clarity
- Cut every unnecessary word before considering length
- Developer-friendly brevity trumps hitting character targets

**Technical limits** (for reference):
- **Articles**: Maximum 354 characters
- **Categories**: Maximum 362 characters
- **Map Topics**: Maximum 362 characters

## Liquid syntax guidelines

**Keep these in intros** (they're acceptable for dynamic content):
- {% data variables.* %} - Product names and variables
- {% data reusables.* %} - Reusable content blocks

**Avoid these in intros** (version-agnostic content preferred):
- {% ifversion %} blocks - Create intros that work across all supported versions

**Common variable meanings** (for analysis purposes):
- {% data variables.product.prodname_github %} = "GitHub"
- {% data variables.product.prodname_ghe_server %} = "GitHub Enterprise Server"
- {% data variables.product.prodname_copilot %} = "GitHub Copilot"
- {% data variables.copilot.copilot_coding_agent %} = "Copilot Coding Agent"

Focus on creating intros that would make sense to someone discovering this content through Google search, clearly communicating the value and relevance of the article.
21 changes: 18 additions & 3 deletions src/ai-tools/scripts/ai-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ interface EditorType {

interface EditorTypes {
versioning: EditorType
intro: EditorType
}

const editorTypes: EditorTypes = {
versioning: {
description: 'Refine versioning according to simplification guidance.',
},
intro: {
description: 'Refine intro frontmatter based on SEO and content guidelines.',
},
}

const refinementDescriptions = (): string => {
Expand Down Expand Up @@ -88,7 +92,7 @@ program

for (const editorType of editors) {
spinner.text = `Running the AI-powered ${editorType} refinement...`
const answer = await callEditor(editorType, content)
const answer = await callEditor(editorType, content, options.write || false)
spinner.stop()

if (options.write) {
Expand Down Expand Up @@ -125,9 +129,20 @@ interface PromptData {
max_tokens?: number
}

async function callEditor(editorType: keyof EditorTypes, content: string): Promise<string> {
async function callEditor(
editorType: keyof EditorTypes,
content: string,
writeMode: boolean,
): Promise<string> {
const markdownPromptPath = path.join(promptDir, `${editorType}.md`)
const markdownPrompt = fs.readFileSync(markdownPromptPath, 'utf8')
let markdownPrompt = fs.readFileSync(markdownPromptPath, 'utf8')

// For intro type in write mode, append special instructions
if (editorType === 'intro' && writeMode) {
markdownPrompt +=
'\n\n**WRITE MODE**: Output only the complete updated file content with the new intro in the frontmatter. Do not include analysis or explanations - just return the file ready to write.'
}

const prompt = yaml.load(fs.readFileSync(promptTemplatePath, 'utf8')) as PromptData

prompt.messages.forEach((msg) => {
Expand Down
1 change: 1 addition & 0 deletions src/events/components/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export function sendEvent<T extends EventType>({
screen_width: window.screen.width,
screen_height: window.screen.height,
pixel_ratio: window.devicePixelRatio || 1,
user_agent: navigator.userAgent,

// Location information
timezone: new Date().getTimezoneOffset() / -60,
Expand Down
8 changes: 8 additions & 0 deletions src/events/lib/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ const context = {
description: 'The device pixel ratio.',
minimum: 0,
},
ip: {
type: 'string',
description: 'The IP address of the user.',
},
user_agent: {
type: 'string',
description: 'The raw user agent string from the browser.',
},

// Location information
timezone: {
Expand Down
4 changes: 4 additions & 0 deletions src/events/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ router.post(
// JSON.stringify removes `undefined` values but not `null`, and we don't want to send `null` to Hydro
body.context.dotcom_user = req.cookies?.dotcom_user ? req.cookies.dotcom_user : undefined
body.context.is_staff = Boolean(req.cookies?.staffonly)
// Add IP address and user agent from request
// Moda forwards the client's IP using the `fastly-client-ip` header
body.context.ip = req.headers['fastly-client-ip'] as string | undefined
body.context.user_agent ??= req.headers['user-agent']
}
const validate = validators[type]
if (!validate(body)) {
Expand Down
6 changes: 6 additions & 0 deletions src/events/tests/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ describe('POST /events', () => {
// Location information
timezone: -7,
user_language: 'en-US',
ip: '192.0.2.1',
user_agent:
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36',
},
}

Expand Down Expand Up @@ -88,6 +91,9 @@ describe('POST /events', () => {
// Location information
timezone: -7,
user_language: 'en-US',
ip: '192.0.2.1',
user_agent:
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36',
},
}

Expand Down
2 changes: 2 additions & 0 deletions src/events/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export type EventProps = {
screen_width?: number
screen_height?: number
pixel_ratio?: number
ip?: string
user_agent?: string
timezone: number
user_language: string
os_preference: string
Expand Down
Loading
Loading