diff --git a/content/copilot/how-tos/use-copilot-agents/coding-agent/create-a-pr.md b/content/copilot/how-tos/use-copilot-agents/coding-agent/create-a-pr.md
index 675e2b061d..b127b82916 100644
--- a/content/copilot/how-tos/use-copilot-agents/coding-agent/create-a-pr.md
+++ b/content/copilot/how-tos/use-copilot-agents/coding-agent/create-a-pr.md
@@ -1,7 +1,7 @@
---
title: Asking GitHub Copilot to create a pull request
shortTitle: Create a PR
-intro: 'You can ask {% data variables.product.prodname_copilot_short %} to create a pull request from many places, including the agents panel, {% data variables.copilot.copilot_chat_short %}, and agentic coding tools and IDEs with Model Context Protocol (MCP) support.'
+intro: 'You can ask {% data variables.product.prodname_copilot_short %} to create a pull request from many places, including the agents panel, {% data variables.copilot.copilot_chat_short %}, the {% data variables.product.prodname_cli %}, and agentic coding tools and IDEs with Model Context Protocol (MCP) support.'
product: '{% data reusables.gated-features.copilot-coding-agent %}
Sign up for {% data variables.product.prodname_copilot_short %} {% octicon "link-external" height:16 %}'
versions:
feature: copilot
@@ -28,6 +28,7 @@ You can ask {% data variables.product.prodname_copilot_short %} to work on a tas
* The agents panel, available across {% data variables.product.github %}
* The agents page on {% data variables.product.github %}
* {% data variables.copilot.copilot_chat_short %} in {% data variables.product.prodname_vscode %}, {% data variables.product.prodname_vs %}, JetBrains IDEs and {% data variables.product.prodname_dotcom_the_website %}
+* The {% data variables.product.prodname_cli %}
* Your preferred IDE or agentic coding tool with Model Context Protocol (MCP) support
* The Raycast launcher on macOS
@@ -100,6 +101,25 @@ You can ask {% data variables.product.prodname_copilot_short %} to open a pull r
{% data variables.product.prodname_copilot_short %} will start a new session and respond with a link to the pull request it creates. It will work on the task and push changes to the pull request, and then add you as a reviewer when it has finished, triggering a notification.
+## Asking {% data variables.product.prodname_copilot_short %} to create a pull request from the {% data variables.product.prodname_cli %}
+
+> [!NOTE]
+> The `agent-task` command set is only available in v2.80.0 or later of the {% data variables.product.prodname_cli %}. This command set is a {% data variables.release-phases.public_preview %} and is subject to change.
+
+You can start a new {% data variables.copilot.copilot_coding_agent %} session with the `gh agent-task create` command in the {% data variables.product.prodname_cli %}.
+
+When you run the command without any arguments, you are asked to enter a prompt. {% data variables.copilot.copilot_coding_agent %} acts on the prompt and opens a pull request in the current repository.
+
+You can use command line options to:
+
+* Provide the prompt (`gh agent-task create "Example prompt"`)
+* Choose a base branch, instead of using the repository's default branch (`--base`)
+* Select a repository, instead of targeting the current repository (`--repo`)
+* Follow the session log in real time (`--follow`)
+
+
+To see all of the available options, run `gh agent-task create --help`.
+
## Asking {% data variables.product.prodname_copilot_short %} to create a pull request from the {% data variables.product.github %} MCP server
As an alternative to using {% data variables.copilot.copilot_chat_short %}, you can use the remote {% data variables.product.github %} MCP server to trigger {% data variables.copilot.copilot_coding_agent %} from any MCP host.
diff --git a/content/copilot/how-tos/use-copilot-agents/coding-agent/track-copilot-sessions.md b/content/copilot/how-tos/use-copilot-agents/coding-agent/track-copilot-sessions.md
index 2ae5306c7b..517a6c4b7a 100644
--- a/content/copilot/how-tos/use-copilot-agents/coding-agent/track-copilot-sessions.md
+++ b/content/copilot/how-tos/use-copilot-agents/coding-agent/track-copilot-sessions.md
@@ -1,7 +1,7 @@
---
title: Tracking GitHub Copilot's sessions
shortTitle: Track Copilot sessions
-intro: 'You can use the agents panel or page, {% data variables.product.prodname_vscode %}, Raycast and session logs to track {% data variables.product.prodname_copilot_short %}''s progress and understand its approach.'
+intro: 'You can use the agents panel or page, {% data variables.product.prodname_vscode %}, the {% data variables.product.prodname_cli %}, Raycast and session logs to track {% data variables.product.prodname_copilot_short %}''s progress and understand its approach.'
product: '{% data reusables.gated-features.copilot-coding-agent %}
Sign up for {% data variables.product.prodname_copilot_short %} {% octicon "link-external" height:16 %}'
versions:
feature: copilot
@@ -24,7 +24,7 @@ contentType: how-tos
After you give {% data variables.product.prodname_copilot_short %} a task, it works autonomously in the background to complete it. See [AUTOTITLE](/copilot/concepts/about-copilot-coding-agent).
-The agents panel, [agents page](https://github.com/copilot/agents), and {% data variables.product.prodname_copilot %} extension for Raycast provide an overview of your agent sessions across repositories. You can use them to kick off new tasks and track {% data variables.product.prodname_copilot_short %}'s progress.
+The agents panel, [agents page](https://github.com/copilot/agents), the {% data variables.product.prodname_cli %}, and {% data variables.product.prodname_copilot %} extension for Raycast provide an overview of your agent sessions across repositories. You can use them to kick off new tasks and track {% data variables.product.prodname_copilot_short %}'s progress.
You can also track {% data variables.product.prodname_copilot_short %}'s sessions in a specific repository from {% data variables.product.prodname_vscode %}.
@@ -46,6 +46,19 @@ To view the session logs, click through to the pull request in the list, then fi
You can also start new agent sessions from the page and panel. See [AUTOTITLE](/copilot/how-tos/agents/copilot-coding-agent/asking-copilot-to-create-a-pull-request).
+## Tracking agent sessions from the {% data variables.product.prodname_cli %}
+
+> [!NOTE]
+> The `agent-task` command set is only available in v2.80.0 or later of the {% data variables.product.prodname_cli %}. This command set is a {% data variables.release-phases.public_preview %} and is subject to change.
+
+You can see a list of your running and past agent sessions from the {% data variables.product.prodname_cli %} with the `gh agent-task list` command. The output will show a list of your recent sessions.
+
+To see more information on a specific session, use the `gh agent-task view` command. For example, to view information about the session associated with pull request #123 in the `monalisa/bookstore` repository, run `gh agent-task view --repo monalisa/bookstore 123`.
+
+To view the session logs, add the `--log` option. Optionally, use the `--follow` option to stream live logs as the agent works.
+
+To see all of the available options, run `gh agent-task list --help` or `gh agent-task view --help`.
+
## Tracking agent sessions from Raycast
{% data reusables.copilot.coding-agent.raycast-intro %}
diff --git a/src/content-linter/lib/diff-files.js b/src/content-linter/lib/diff-files.ts
similarity index 94%
rename from src/content-linter/lib/diff-files.js
rename to src/content-linter/lib/diff-files.ts
index 1617ca5668..a029dcb02d 100644
--- a/src/content-linter/lib/diff-files.js
+++ b/src/content-linter/lib/diff-files.ts
@@ -5,14 +5,14 @@ import fs from 'fs'
// is unpredictable in GitHub Actions because of how it does `git clone`.
// So we rely on environment variables instead.
-export function getDiffFiles() {
+export function getDiffFiles(): string[] {
// Instead of testing every single file possible, if there's
// an environment variable called `DIFF_FILES` or one called
// `DIFF_FILE` then use that.
// If `DIFF_FILES` is set, it's expected to be a space separated
// string. If `DIFF_FILE` is set, it's expected to be a text file
// which contains a space separated string.
- const diffFiles = []
+ const diffFiles: string[] = []
// Setting an environment variable called `DIFF_FILES` is optional.
// But if and only if it's set, we will respect it.
// And if it set, turn it into a cleaned up Set so it's made available
diff --git a/src/content-linter/lib/linting-rules/code-fence-line-length.js b/src/content-linter/lib/linting-rules/code-fence-line-length.ts
similarity index 55%
rename from src/content-linter/lib/linting-rules/code-fence-line-length.js
rename to src/content-linter/lib/linting-rules/code-fence-line-length.ts
index 9aa3b7ea06..16b3ef9c23 100644
--- a/src/content-linter/lib/linting-rules/code-fence-line-length.js
+++ b/src/content-linter/lib/linting-rules/code-fence-line-length.ts
@@ -1,20 +1,24 @@
+// @ts-ignore - markdownlint-rule-helpers doesn't provide TypeScript declarations
import { addError, filterTokens, newLineRe } from 'markdownlint-rule-helpers'
-export const codeFenceLineLength = {
+import type { RuleParams, RuleErrorCallback, MarkdownToken, Rule } from '@/content-linter/types'
+
+export const codeFenceLineLength: Rule = {
names: ['GHD030', 'code-fence-line-length'],
description: 'Code fence lines should not exceed a maximum length',
tags: ['code', 'accessibility'],
parser: 'markdownit',
- function: (params, onError) => {
- const MAX_LINE_LENGTH = String(params.config.maxLength || 60)
- filterTokens(params, 'fence', (token) => {
- const lines = token.content.split(newLineRe)
- lines.forEach((line, index) => {
+ function: (params: RuleParams, onError: RuleErrorCallback) => {
+ const MAX_LINE_LENGTH: number = params.config?.maxLength || 60
+ filterTokens(params, 'fence', (token: MarkdownToken) => {
+ if (!token.content) return
+ const lines: string[] = token.content.split(newLineRe)
+ lines.forEach((line: string, index: number) => {
if (line.length > MAX_LINE_LENGTH) {
// The token line number is the line number of the first line of the
// code fence. We want to report the line number of the content within
// the code fence so we need to add 1 + the index.
- const lineNumber = token.lineNumber + index + 1
+ const lineNumber: number = token.lineNumber + index + 1
addError(
onError,
lineNumber,
diff --git a/src/content-render/liquid/post.js b/src/content-render/liquid/post.ts
similarity index 62%
rename from src/content-render/liquid/post.js
rename to src/content-render/liquid/post.ts
index 9f17fb426e..e618d58015 100644
--- a/src/content-render/liquid/post.js
+++ b/src/content-render/liquid/post.ts
@@ -1,16 +1,16 @@
// used below to remove extra newlines in TOC lists
-const endLine = '\r?\n'
-const blankLine = '\\s*?[\r\n]*'
-const startNextLine = '[^\\S\r\n]*?[-\\*] foo
@@ -22,7 +22,7 @@ function cleanUpListEmptyLines(template) {
return template
}
-function cleanUpExtraEmptyLines(template) {
+function cleanUpExtraEmptyLines(template: string): string {
// this removes any extra newlines left by (now resolved) liquid
// statements so that extra space doesn't mess with list numbering
template = template.replace(/(\r?\n){3}/g, '\n\n')
diff --git a/src/content-render/liquid/prompt.js b/src/content-render/liquid/prompt.ts
similarity index 51%
rename from src/content-render/liquid/prompt.js
rename to src/content-render/liquid/prompt.ts
index cc81688f93..a01ebfaedf 100644
--- a/src/content-render/liquid/prompt.js
+++ b/src/content-render/liquid/prompt.ts
@@ -1,17 +1,26 @@
-// src/content-render/liquid/prompt.js
+// src/content-render/liquid/prompt.ts
// Defines {% prompt %}…{% endprompt %} to wrap its content in and append the Copilot icon.
+// @ts-ignore - @primer/octicons doesn't provide TypeScript declarations
import octicons from '@primer/octicons'
-export const Prompt = {
+interface LiquidTag {
+ type: 'block'
+ templates?: any[] // Note: Using 'any' because liquidjs doesn't provide proper types for template objects
+ // Note: Using 'any' for liquid-related parameters because liquidjs doesn't provide comprehensive TypeScript definitions
+ parse(tagToken: any, remainTokens: any): void
+ render(scope: any): Generator
+}
+
+export const Prompt: LiquidTag = {
type: 'block',
// Collect everything until {% endprompt %}
- parse(tagToken, remainTokens) {
+ parse(tagToken: any, remainTokens: any): void {
this.templates = []
const stream = this.liquid.parser.parseStream(remainTokens)
stream
- .on('template', (tpl) => this.templates.push(tpl))
+ .on('template', (tpl: any) => this.templates.push(tpl))
.on('tag:endprompt', () => stream.stop())
.on('end', () => {
throw new Error(`{% prompt %} tag not closed`)
@@ -20,12 +29,12 @@ export const Prompt = {
},
// Render the inner Markdown, wrap in , then append the SVG
- render: function* (scope) {
+ render: function* (scope: any): Generator {
const content = yield this.liquid.renderer.renderTemplates(this.templates, scope)
// build a URL with the prompt text encoded as query parameter
- const promptParam = encodeURIComponent(content)
- const href = `https://github.com/copilot?prompt=${promptParam}`
+ const promptParam: string = encodeURIComponent(content as string)
+ const href: string = `https://github.com/copilot?prompt=${promptParam}`
return `${content}${octicons.copilot.toSVG()}`
},
}
diff --git a/src/content-render/tests/prompt.js b/src/content-render/tests/prompt.ts
similarity index 67%
rename from src/content-render/tests/prompt.js
rename to src/content-render/tests/prompt.ts
index 029a0ef9d4..2eb453990d 100644
--- a/src/content-render/tests/prompt.js
+++ b/src/content-render/tests/prompt.ts
@@ -3,8 +3,8 @@ import { renderContent } from '@/content-render/index'
describe('prompt tag', () => {
test('wraps content in and appends svg', async () => {
- const input = 'Here is your prompt: {% prompt %}example prompt text{% endprompt %}.'
- const output = await renderContent(input)
+ const input: string = 'Here is your prompt: {% prompt %}example prompt text{% endprompt %}.'
+ const output: string = await renderContent(input)
expect(output).toContain('example prompt text node.type === 'code' && node.lang
-
-export default function parseInfoString() {
- return (tree) => {
- visit(tree, matcher, (node) => {
- node.meta = strToObj(node.meta)
-
- // Temporary, remove {:copy} to avoid highlight parse error in translations.
- node.lang = node.lang.replace('{:copy}', '')
- })
- }
-}
-
-function strToObj(str) {
- if (!str) return {}
- return Object.fromEntries(
- str
- .split(/\s+/g)
- .map((k) => k.split(/[:=]/)) // split by colon or equals sign
- .map(([k, ...v]) => [k, v.length ? v.join(':') : true]),
- )
-}
diff --git a/src/content-render/unified/parse-info-string.ts b/src/content-render/unified/parse-info-string.ts
new file mode 100644
index 0000000000..ef494bfd4c
--- /dev/null
+++ b/src/content-render/unified/parse-info-string.ts
@@ -0,0 +1,45 @@
+// Based on https://spec.commonmark.org/0.30/#info-string
+// Parse out info strings on fenced code blocks, example:
+// ```javascript lineNumbers:left copy:all annotate
+// becomes...
+// node.lang = javascript
+// node.meta = { lineNumbers: 'left', copy: 'all', annotate: true }
+// Also parse equals signs, where id=some-id becomes { id: 'some-id' }
+
+import { visit } from 'unist-util-visit'
+
+interface CodeNode {
+ type: 'code'
+ lang?: string
+ meta?: string | Record
+ value: string
+}
+
+// Note: Using 'any' for node because unist-util-visit's type constraints
+// don't easily allow for proper code node typing without complex generics
+const matcher = (node: any): node is CodeNode => node.type === 'code' && node.lang
+
+export default function parseInfoString() {
+ // Note: Using 'any' for tree because unified's AST types are complex and
+ // this function works with different tree types depending on the processor
+ return (tree: any) => {
+ visit(tree, matcher, (node: CodeNode) => {
+ node.meta = strToObj(node.meta as string)
+
+ // Temporary, remove {:copy} to avoid highlight parse error in translations.
+ if (node.lang) {
+ node.lang = node.lang.replace('{:copy}', '')
+ }
+ })
+ }
+}
+
+function strToObj(str?: string): Record {
+ if (!str) return {}
+ return Object.fromEntries(
+ str
+ .split(/\s+/g)
+ .map((k: string) => k.split(/[:=]/)) // split by colon or equals sign
+ .map(([k, ...v]: string[]) => [k, v.length ? v.join(':') : true]),
+ )
+}
diff --git a/src/content-render/unified/use-english-headings.js b/src/content-render/unified/use-english-headings.js
deleted file mode 100644
index 73b7c93fb5..0000000000
--- a/src/content-render/unified/use-english-headings.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import GithubSlugger from 'github-slugger'
-import { encode } from 'html-entities'
-import { toString } from 'hast-util-to-string'
-import { visit } from 'unist-util-visit'
-const slugger = new GithubSlugger()
-
-const matcher = (node) => node.type === 'element' && ['h2', 'h3', 'h4'].includes(node.tagName)
-
-// replace translated IDs and links in headings with English
-export default function useEnglishHeadings({ englishHeadings }) {
- if (!englishHeadings) return
- return (tree) => {
- visit(tree, matcher, (node) => {
- slugger.reset()
- // Get the plain text content of the heading node
- const text = toString(node)
- // find English heading in the collection
- const englishHeading = englishHeadings[encode(text)]
- // get English slug
- const englishSlug = slugger.slug(englishHeading)
- // use English slug for heading ID and link
- if (englishSlug) {
- // only use English slug if there is one, otherwise we'll end up with
- // empty IDs
- node.properties.id = englishSlug
- }
- })
- }
-}
diff --git a/src/content-render/unified/use-english-headings.ts b/src/content-render/unified/use-english-headings.ts
new file mode 100644
index 0000000000..47b90c4e63
--- /dev/null
+++ b/src/content-render/unified/use-english-headings.ts
@@ -0,0 +1,41 @@
+import GithubSlugger from 'github-slugger'
+import { encode } from 'html-entities'
+import { toString } from 'hast-util-to-string'
+import { visit } from 'unist-util-visit'
+
+const slugger = new GithubSlugger()
+
+// Note: Using 'any' for node because the unist/hast type system is complex and
+// the visit function's type constraints don't easily allow for proper element typing
+// without extensive type gymnastics. The runtime check ensures type safety.
+const matcher = (node: any) => node.type === 'element' && ['h2', 'h3', 'h4'].includes(node.tagName)
+
+interface UseEnglishHeadingsOptions {
+ englishHeadings?: Record
+}
+
+// replace translated IDs and links in headings with English
+export default function useEnglishHeadings({ englishHeadings }: UseEnglishHeadingsOptions) {
+ if (!englishHeadings) return
+ // Note: Using 'any' for tree because unified's AST types are complex and
+ // this function works with different tree types depending on the processor
+ return (tree: any) => {
+ // Note: Using 'any' for node because visit() callback typing is restrictive
+ // and doesn't easily allow for proper element typing without complex generics
+ visit(tree, matcher, (node: any) => {
+ slugger.reset()
+ // Get the plain text content of the heading node
+ const text: string = toString(node)
+ // find English heading in the collection
+ const englishHeading: string = englishHeadings[encode(text)]
+ // get English slug
+ const englishSlug: string = slugger.slug(englishHeading)
+ // use English slug for heading ID and link
+ if (englishSlug) {
+ // only use English slug if there is one, otherwise we'll end up with
+ // empty IDs
+ node.properties.id = englishSlug
+ }
+ })
+ }
+}
diff --git a/src/github-apps/scripts/enabled-list-schema.js b/src/github-apps/scripts/enabled-list-schema.ts
similarity index 67%
rename from src/github-apps/scripts/enabled-list-schema.js
rename to src/github-apps/scripts/enabled-list-schema.ts
index 1d805d62af..75ee13712d 100644
--- a/src/github-apps/scripts/enabled-list-schema.js
+++ b/src/github-apps/scripts/enabled-list-schema.ts
@@ -3,7 +3,23 @@
// src/github-apps/data/user-to-server-rest.json
// and src/github-apps/data/fine-grained-pat.json
-export default {
+interface SchemaProperty {
+ description: string
+ type: string
+}
+
+interface EnabledListSchema {
+ type: string
+ required: string[]
+ properties: {
+ slug: SchemaProperty
+ subcategory: SchemaProperty
+ verb: SchemaProperty
+ requestPath: SchemaProperty
+ }
+}
+
+const schema: EnabledListSchema = {
type: 'object',
required: ['slug', 'subcategory', 'verb', 'requestPath'],
properties: {
@@ -25,3 +41,5 @@ export default {
},
},
}
+
+export default schema
diff --git a/src/rest/api/anchor-redirect.js b/src/rest/api/anchor-redirect.ts
similarity index 72%
rename from src/rest/api/anchor-redirect.js
rename to src/rest/api/anchor-redirect.ts
index 6eb0c3b122..e29a72b056 100644
--- a/src/rest/api/anchor-redirect.js
+++ b/src/rest/api/anchor-redirect.ts
@@ -7,12 +7,14 @@ import { REST_DATA_DIR } from '../lib/index'
const clientSideRestAPIRedirects = readCompressedJsonFileFallbackLazily(
path.join(REST_DATA_DIR, 'client-side-rest-api-redirects.json'),
-)
+) as () => Record
const router = express.Router()
// Returns a client side redirect if one exists for the given path.
-router.get('/', function redirects(req, res) {
+// Note: Using 'any' for req/res because Express types are complex and the
+// function signature is constrained by the router.get() overloads
+router.get('/', function redirects(req: any, res: any) {
if (!req.query.path) {
return res.status(400).send("Missing 'path' query string")
}
@@ -22,7 +24,7 @@ router.get('/', function redirects(req, res) {
defaultCacheControl(res)
- const redirectFrom = `${req.query.path}#${req.query.hash}`
+ const redirectFrom: string = `${req.query.path}#${req.query.hash}`
res.status(200).send({ to: clientSideRestAPIRedirects()[redirectFrom] })
})
diff --git a/src/tests/helpers/schemas/site-tree-schema.js b/src/tests/helpers/schemas/site-tree-schema.js
deleted file mode 100644
index 92e15e426c..0000000000
--- a/src/tests/helpers/schemas/site-tree-schema.js
+++ /dev/null
@@ -1,27 +0,0 @@
-const childPage = {
- type: 'object',
- required: ['href', 'page'],
- properties: {
- href: {
- type: 'string',
- },
- page: {
- type: 'object',
- required: ['title', 'relativePath', 'permalinks'],
- properties: {
- title: {
- type: 'string',
- },
- relativePath: {
- type: 'string',
- },
- permalinks: {
- type: 'array',
- minItems: 1,
- },
- },
- },
- },
-}
-
-export default { childPage }
diff --git a/src/tests/helpers/schemas/site-tree-schema.ts b/src/tests/helpers/schemas/site-tree-schema.ts
new file mode 100644
index 0000000000..40e3722959
--- /dev/null
+++ b/src/tests/helpers/schemas/site-tree-schema.ts
@@ -0,0 +1,55 @@
+interface SchemaProperty {
+ type: string
+ minItems?: number
+}
+
+interface PageProperties {
+ title: SchemaProperty
+ relativePath: SchemaProperty
+ permalinks: SchemaProperty
+}
+
+interface PageSchema {
+ type: string
+ required: string[]
+ properties: PageProperties
+}
+
+interface ChildPageProperties {
+ href: SchemaProperty
+ page: PageSchema
+}
+
+interface ChildPageSchema {
+ type: string
+ required: string[]
+ properties: ChildPageProperties
+}
+
+const childPage: ChildPageSchema = {
+ type: 'object',
+ required: ['href', 'page'],
+ properties: {
+ href: {
+ type: 'string',
+ },
+ page: {
+ type: 'object',
+ required: ['title', 'relativePath', 'permalinks'],
+ properties: {
+ title: {
+ type: 'string',
+ },
+ relativePath: {
+ type: 'string',
+ },
+ permalinks: {
+ type: 'array',
+ minItems: 1,
+ },
+ },
+ },
+ },
+}
+
+export default { childPage }