Skip to content

Commit d79d9d3

Browse files
authored
Merge pull request #45913 from github/repo-sync
Repo sync
2 parents 976a10b + 74c7d20 commit d79d9d3

261 files changed

Lines changed: 616 additions & 2253 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.

‎src/ai-tools/lib/auth-utils.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { execSync } from 'child_process'
22

33
/**
4-
* Ensure GitHub token is available, exiting process if not found
4+
* Falls back to the gh CLI token, setting process.env.GITHUB_TOKEN.
5+
* Exits the process if neither is available.
56
*/
67
export function ensureGitHubToken(): void {
78
if (!process.env.GITHUB_TOKEN) {

‎src/ai-tools/lib/call-models-api.ts‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export async function callModelsApi(
4242
): Promise<string> {
4343
let aiResponse: ChatCompletionChoice
4444

45-
// Set default model if none specified
4645
if (!promptWithContent.model) {
4746
promptWithContent.model = DEFAULT_MODEL
4847
if (verbose) {
@@ -51,7 +50,6 @@ export async function callModelsApi(
5150
}
5251

5352
try {
54-
// Create an AbortController for timeout handling
5553
const controller = new AbortController()
5654
const timeoutId = setTimeout(() => controller.abort(), API_TIMEOUT_MS)
5755

@@ -82,7 +80,6 @@ export async function callModelsApi(
8280
if (!response.ok) {
8381
let errorMessage = `HTTP error! status: ${response.status} - ${response.statusText}`
8482

85-
// Try to get more detailed error information
8683
try {
8784
const errorBody = await response.json()
8885
if (errorBody.error && errorBody.error.message) {
@@ -92,7 +89,6 @@ export async function callModelsApi(
9289
// If we can't parse error body, continue with basic error
9390
}
9491

95-
// Add helpful hints for common errors
9692
if (response.status === 401) {
9793
errorMessage += ' (Check your GITHUB_TOKEN)'
9894
} else if (response.status === 400) {
@@ -135,9 +131,7 @@ export async function callModelsApi(
135131
return cleanAIResponse(aiResponse.message.content)
136132
}
137133

138-
// Helper function to clean up AI response content
139134
function cleanAIResponse(content: string): string {
140-
// Remove markdown code blocks
141135
return content
142136
.replace(/^```[\w]*\n/gm, '') // Remove opening code blocks
143137
.replace(/\n```$/gm, '') // Remove closing code blocks at end

‎src/ai-tools/lib/file-utils.ts‎

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ import { schema } from '@/frame/lib/frontmatter'
66

77
const MAX_DIRECTORY_DEPTH = 20
88

9-
/**
10-
* Enhanced recursive markdown file finder with symlink, depth, and root path checks
11-
*/
129
export function findMarkdownFiles(
1310
dir: string,
1411
rootDir: string,
@@ -33,7 +30,6 @@ export function findMarkdownFiles(
3330
return []
3431
}
3532
visited.add(realDir)
36-
// Prevent excessive depth
3733
if (depth > maxDepth) {
3834
return []
3935
}
@@ -71,9 +67,8 @@ interface FrontmatterProperties {
7167
}
7268

7369
/**
74-
* Function to merge new frontmatter properties into existing file while preserving formatting.
75-
* Uses surgical replacement to only modify the specific field(s) being updated,
76-
* preserving all original YAML formatting for unchanged fields.
70+
* Replaces only the fields being updated, so the original YAML formatting
71+
* of every other field survives.
7772
*/
7873
export function mergeFrontmatterProperties(filePath: string, newPropertiesYaml: string): string {
7974
const content = fs.readFileSync(filePath, 'utf8')
@@ -90,7 +85,7 @@ export function mergeFrontmatterProperties(filePath: string, newPropertiesYaml:
9085
}
9186

9287
try {
93-
// Clean up the AI response - remove markdown code blocks if present
88+
// The model often wraps its output in a code fence.
9489
let cleanedYaml = newPropertiesYaml.trim()
9590
cleanedYaml = cleanedYaml.replace(/^```ya?ml\s*\n/i, '')
9691
cleanedYaml = cleanedYaml.replace(/\n```\s*$/i, '')
@@ -111,12 +106,10 @@ export function mergeFrontmatterProperties(filePath: string, newPropertiesYaml:
111106
}),
112107
)
113108

114-
// Split content into lines for surgical replacement
115109
const lines = content.split('\n')
116110
let inFrontmatter = false
117111
let frontmatterEndIndex = -1
118112

119-
// Find frontmatter boundaries
120113
for (let i = 0; i < lines.length; i++) {
121114
if (lines[i].trim() === '---') {
122115
if (!inFrontmatter) {
@@ -128,17 +121,14 @@ export function mergeFrontmatterProperties(filePath: string, newPropertiesYaml:
128121
}
129122
}
130123

131-
// Replace each field value while preserving everything else
132124
for (const [key, value] of Object.entries(sanitizedProperties)) {
133125
const formattedValue = typeof value === 'string' ? `'${value.replace(/'/g, "''")}'` : value
134126

135-
// Find the line with this field
136127
let foundField = false
137128
for (let i = 1; i < frontmatterEndIndex; i++) {
138129
const line = lines[i]
139130
if (line.startsWith(`${key}:`)) {
140131
foundField = true
141-
// Simple replacement: keep the field name and spacing, replace the value
142132
const colonIndex = line.indexOf(':')
143133
const leadingSpace = line.substring(colonIndex + 1, colonIndex + 2) // Usually a space
144134
lines[i] = `${key}:${leadingSpace}${formattedValue}`
@@ -153,7 +143,6 @@ export function mergeFrontmatterProperties(filePath: string, newPropertiesYaml:
153143
}
154144
}
155145

156-
// If field doesn't exist, add it before the closing ---
157146
if (!foundField && frontmatterEndIndex > 0) {
158147
lines.splice(frontmatterEndIndex, 0, `${key}: ${formattedValue}`)
159148
frontmatterEndIndex++

‎src/ai-tools/lib/prompt-utils.ts‎

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,11 @@ export interface PromptData {
1717
max_tokens?: number
1818
}
1919

20-
/**
21-
* Get the prompts directory path
22-
*/
2320
export function getPromptsDir(): string {
2421
const __dirname = path.dirname(fileURLToPath(import.meta.url))
2522
return path.join(__dirname, '../prompts')
2623
}
2724

28-
/**
29-
* Dynamically discover available editor types from prompt files
30-
*/
3125
export function getAvailableEditorTypes(promptDir: string): string[] {
3226
const editorTypes: string[] = []
3327

@@ -46,16 +40,10 @@ export function getAvailableEditorTypes(promptDir: string): string[] {
4640
return editorTypes
4741
}
4842

49-
/**
50-
* Get formatted description of available refinement types
51-
*/
5243
export function getRefinementDescriptions(editorTypes: string[]): string {
5344
return editorTypes.join(', ')
5445
}
5546

56-
/**
57-
* Enrich context for intro prompt on index.md files
58-
*/
5947
export function enrichIndexContext(filePath: string, content: string): string {
6048
if (!filePath.endsWith('index.md')) return content
6149

@@ -72,7 +60,6 @@ export function enrichIndexContext(filePath: string, content: string): string {
7260
.join(' ')
7361
: ''
7462

75-
// Get child article titles
7663
const titles: string[] = []
7764
if (data.children && Array.isArray(data.children)) {
7865
const dir = path.dirname(filePath)
@@ -94,7 +81,6 @@ export function enrichIndexContext(filePath: string, content: string): string {
9481
}
9582
}
9683

97-
// Build context note
9884
const parts: string[] = []
9985
if (productName) parts.push(`Product: ${productName}`)
10086
if (titles.length > 0) parts.push(`Child articles: ${titles.join(', ')}`)
@@ -114,24 +100,19 @@ export function enrichIndexContext(filePath: string, content: string): string {
114100
return content
115101
}
116102

117-
/**
118-
* Call an editor with the given content and options
119-
*/
120103
export async function callEditor(
121104
editorType: string,
122105
content: string,
123106
promptDir: string,
124107
writeMode: boolean,
125108
verbose = false,
126-
promptContent?: string, // Optional: use this instead of reading from file
109+
promptContent?: string, // Use this instead of reading a prompt file
127110
): Promise<string> {
128111
let markdownPrompt: string
129112

130113
if (promptContent) {
131-
// Use provided prompt content (e.g., from Copilot Space)
132114
markdownPrompt = promptContent
133115
} else {
134-
// Read from file
135116
const markdownPromptPath = path.join(promptDir, `${editorType}.md`)
136117

137118
if (!fs.existsSync(markdownPromptPath)) {
@@ -144,15 +125,15 @@ export async function callEditor(
144125

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

147-
// Validate the prompt template has required properties
148128
if (!prompt.messages || !Array.isArray(prompt.messages)) {
149129
throw new Error('Invalid prompt template: missing or invalid messages array')
150130
}
151131

152132
for (const msg of prompt.messages) {
153133
msg.content = msg.content.replace('{{markdownPrompt}}', markdownPrompt)
154134
msg.content = msg.content.replace('{{input}}', content)
155-
// Replace writeMode template variable with simple string replacement
135+
// Resolve the write-mode markers, then strip whatever they marked
136+
// for removal.
156137
msg.content = msg.content.replace(
157138
/<!-- IF_WRITE_MODE -->/g,
158139
writeMode ? '' : '<!-- REMOVE_START -->',
@@ -166,7 +147,6 @@ export async function callEditor(
166147
writeMode ? '' : '<!-- REMOVE_END -->',
167148
)
168149

169-
// Remove sections marked for removal
170150
msg.content = msg.content.replace(/<!-- REMOVE_START -->[\s\S]*?<!-- REMOVE_END -->/g, '')
171151
}
172152

‎src/ai-tools/lib/spaces-utils.ts‎

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
/**
2-
* Copilot Space API response types
3-
*/
41
import { fetchWithRetry, readBodyWithTimeout } from '@/frame/lib/fetch-utils'
52

63
export interface SpaceResource {
@@ -25,9 +22,6 @@ export interface SpaceData {
2522
updated_at: string
2623
}
2724

28-
/**
29-
* Parse a Copilot Space URL to extract org and space ID
30-
*/
3125
export function parseSpaceUrl(url: string): { org: string; id: string } {
3226
// Expected format: https://api.github.com/orgs/{org}/copilot-spaces/{id}
3327
const match = url.match(/\/orgs\/([^/]+)\/copilot-spaces\/(\d+)/)
@@ -44,9 +38,6 @@ export function parseSpaceUrl(url: string): { org: string; id: string } {
4438
}
4539
}
4640

47-
/**
48-
* Fetch a Copilot Space from the GitHub API
49-
*/
5041
export async function fetchCopilotSpace(spaceUrl: string): Promise<SpaceData> {
5142
const { org, id } = parseSpaceUrl(spaceUrl)
5243
const apiUrl = `https://api.github.com/orgs/${org}/copilot-spaces/${id}`
@@ -88,26 +79,20 @@ export async function fetchCopilotSpace(spaceUrl: string): Promise<SpaceData> {
8879
return (await readBodyWithTimeout(response, () => response.json(), 30_000)) as SpaceData
8980
}
9081

91-
/**
92-
* Convert a Copilot Space to a markdown prompt file
93-
*/
9482
export function convertSpaceToPrompt(space: SpaceData): string {
9583
const timestamp = new Date().toISOString()
9684
const lines: string[] = []
9785

98-
// Header with metadata
9986
lines.push(`<!-- Generated from Copilot Space: ${space.name} -->`)
10087
lines.push(`<!-- Space ID: ${space.number} | Generated: ${timestamp} -->`)
10188
lines.push(`<!-- Space URL: ${space.html_url} -->`)
10289
lines.push('')
10390

104-
// General instructions
10591
if (space.general_instructions) {
10692
lines.push(space.general_instructions.trim())
10793
lines.push('')
10894
}
10995

110-
// Add each resource as a context section
11196
if (space.resources_attributes && space.resources_attributes.length > 0) {
11297
for (const resource of space.resources_attributes) {
11398
if (resource.resource_type === 'free_text' && resource.metadata) {

0 commit comments

Comments
 (0)