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
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4351,6 +4351,7 @@
"@shikijs/monaco": "^3.7.0",
"@types/chai": "^4.1.4",
"@types/glob": "7.1.3",
"@types/js-yaml": "^4.0.9",
"@types/lru-cache": "^5.1.0",
"@types/marked": "^0.7.2",
"@types/mocha": "^8.2.2",
Expand Down Expand Up @@ -4432,6 +4433,7 @@
"debounce": "^1.2.1",
"events": "3.2.0",
"fast-deep-equal": "^3.1.3",
"js-yaml": "^4.1.1",
"jsonc-parser": "^3.3.1",
"jszip": "^3.10.1",
"lru-cache": "6.0.0",
Expand All @@ -4452,4 +4454,4 @@
"string_decoder": "^1.3.0"
},
"license": "MIT"
}
}
1 change: 1 addition & 0 deletions src/@types/vscode.proposed.chatParticipantAdditions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ declare module 'vscode' {
isComplete?: boolean;
toolSpecificData?: ChatTerminalToolInvocationData;
fromSubAgent?: boolean;
presentation?: 'hidden' | 'hiddenAfterComplete' | undefined;

constructor(toolName: string, toolCallId: string, isError?: boolean);
}
Expand Down
11 changes: 7 additions & 4 deletions src/github/folderRepositoryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1346,10 +1346,13 @@ export class FolderRepositoryManager extends Disposable {
}

async getIssueTemplates(): Promise<vscode.Uri[]> {
const pattern = '{docs,.github}/ISSUE_TEMPLATE/*.md';
return vscode.workspace.findFiles(
new vscode.RelativePattern(this._repository.rootUri, pattern), null
);
const mdPattern = '{docs,.github}/ISSUE_TEMPLATE/*.md';
const ymlPattern = '{docs,.github}/ISSUE_TEMPLATE/*.yml';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it not be more efficient to do '{docs,.github}/ISSUE_TEMPLATE/*.{md,yml}' as a single pattern?

const [mdTemplates, ymlTemplates] = await Promise.all([
vscode.workspace.findFiles(new vscode.RelativePattern(this._repository.rootUri, mdPattern), null),
vscode.workspace.findFiles(new vscode.RelativePattern(this._repository.rootUri, ymlPattern), null)
]);
return [...mdTemplates, ...ymlTemplates];
}

async getPullRequestTemplateBody(owner: string): Promise<string | undefined> {
Expand Down
67 changes: 67 additions & 0 deletions src/issues/issueFeatureRegistrar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { basename } from 'path';
import * as yaml from 'js-yaml';
import * as vscode from 'vscode';
import { CurrentIssue } from './currentIssue';
import { IssueCompletionProvider } from './issueCompletionProvider';
Expand Down Expand Up @@ -56,6 +57,7 @@ import {
PermalinkInfo,
pushAndCreatePR,
USER_EXPRESSION,
YamlIssueTemplate,
} from './util';
import { truncate } from '../common/utils';
import { OctokitCommon } from '../github/common';
Expand Down Expand Up @@ -1250,13 +1252,78 @@ ${options?.body ?? ''}\n
}

private getDataFromTemplate(template: string): IssueTemplate {
// Try to parse as YAML first (YAML templates have a different structure)
try {
const parsed = yaml.load(template);
// Check if it looks like a YAML issue template (has name and body fields)
if (parsed && typeof parsed === 'object' && (parsed as YamlIssueTemplate).name && (parsed as YamlIssueTemplate).body) {
// This is a YAML template
return this.parseYamlTemplate(parsed as YamlIssueTemplate);
}
} catch (e) {
// Not a valid YAML, continue to Markdown parsing
}

// Parse as Markdown frontmatter template
const title = template.match(/title:\s*(.*)/)?.[1]?.replace(/^["']|["']$/g, '');
const name = template.match(/name:\s*(.*)/)?.[1]?.replace(/^["']|["']$/g, '');
const about = template.match(/about:\s*(.*)/)?.[1]?.replace(/^["']|["']$/g, '');
const body = template.match(/---([\s\S]*)---([\s\S]*)/)?.[2];
return { title, name, about, body };
}

private parseYamlTemplate(parsed: YamlIssueTemplate): IssueTemplate {
const name = parsed.name;
const about = parsed.description || parsed.about;
const title = parsed.title;

// Convert YAML body fields to markdown
let body = '';
if (parsed.body && Array.isArray(parsed.body)) {
for (const field of parsed.body) {
if (field.type === 'markdown' && field.attributes?.value) {
body += field.attributes.value + '\n\n';
} else if (field.type === 'textarea' && field.attributes?.label) {
body += `## ${field.attributes.label}\n\n`;
if (field.attributes.description) {
body += `${field.attributes.description}\n\n`;
}
if (field.attributes.placeholder) {
body += `${field.attributes.placeholder}\n\n`;
} else if (field.attributes.value) {
body += `${field.attributes.value}\n\n`;
}
} else if (field.type === 'input' && field.attributes?.label) {
body += `## ${field.attributes.label}\n\n`;
if (field.attributes.description) {
body += `${field.attributes.description}\n\n`;
}
if (field.attributes.placeholder) {
body += `${field.attributes.placeholder}\n\n`;
}
} else if (field.type === 'dropdown' && field.attributes?.label) {
body += `## ${field.attributes.label}\n\n`;
if (field.attributes.description) {
body += `${field.attributes.description}\n\n`;
}
if (field.attributes.options && Array.isArray(field.attributes.options)) {
body += field.attributes.options.map((opt: string | { label?: string }) => typeof opt === 'string' ? `- ${opt}` : `- ${opt.label || ''}`).join('\n') + '\n\n';
}
} else if (field.type === 'checkboxes' && field.attributes?.label) {
body += `## ${field.attributes.label}\n\n`;
if (field.attributes.description) {
body += `${field.attributes.description}\n\n`;
}
if (field.attributes.options && Array.isArray(field.attributes.options)) {
body += field.attributes.options.map((opt: { label?: string } | string) => `- [ ] ${typeof opt === 'string' ? opt : opt.label || ''}`).join('\n') + '\n\n';
}
}
}
}

return { title, name, about, body: body.trim() || undefined };
}

private async doCreateIssue(
document: vscode.TextDocument | undefined,
newIssue: NewIssue | undefined,
Expand Down
25 changes: 25 additions & 0 deletions src/issues/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,31 @@ export interface IssueTemplate {
body: string | undefined
}

export interface YamlIssueTemplate {
name?: string;
description?: string;
about?: string;
title?: string;
labels?: string[];
assignees?: string[];
body?: YamlTemplateField[];
}

export interface YamlTemplateField {
type: 'markdown' | 'textarea' | 'input' | 'dropdown' | 'checkboxes';
id?: string;
attributes?: {
label?: string;
description?: string;
placeholder?: string;
value?: string;
options?: (string | { label?: string; required?: boolean })[];
};
validations?: {
required?: boolean;
};
}

const HEAD = 'HEAD';
const UPSTREAM = 1;
const UPS = 2;
Expand Down
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,11 @@
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-lib-report" "*"

"@types/js-yaml@^4.0.9":
version "4.0.9"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2"
integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==

"@types/json-schema@^7.0.15":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
Expand Down Expand Up @@ -4760,6 +4765,13 @@ [email protected], js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"

js-yaml@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b"
integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
dependencies:
argparse "^2.0.1"

[email protected]:
version "3.0.2"
resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-3.0.2.tgz#6bd299c13b0c4626b2da2c0393cd4385d606acb9"
Expand Down