fix(coding-agent): honor comma-separated and **/* Copilot applyTo globs
GitHub documents applyTo as a single comma-separated string (e.g. "**/*.ts,**/*.tsx") and treats both ** and **/* as all-files. The rule loader kept the whole CSV value as one glob and missed **/* for always-apply. Split applyTo via parseCSV and include **/* as all-files. Addresses review feedback on #2734.
This commit is contained in:
@@ -216,22 +216,16 @@ function transformInstructionRule(
|
||||
}
|
||||
|
||||
function normalizeApplyToGlobs(value: unknown): string[] | undefined {
|
||||
if (typeof value === "string") {
|
||||
const glob = value.trim();
|
||||
return glob ? [glob] : undefined;
|
||||
}
|
||||
if (!Array.isArray(value)) {
|
||||
return undefined;
|
||||
}
|
||||
const globs = value
|
||||
.filter((item): item is string => typeof item === "string")
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
// GitHub documents applyTo as a single comma-separated string (e.g.
|
||||
// "**/*.ts,**/*.tsx"); also tolerate a YAML array of such strings.
|
||||
const raw = Array.isArray(value) ? value : [value];
|
||||
const globs = raw.flatMap(item => (typeof item === "string" ? parseCSV(item) : []));
|
||||
return globs.length > 0 ? globs : undefined;
|
||||
}
|
||||
|
||||
function isAlwaysApplyGlob(glob: string): boolean {
|
||||
return glob === "*" || glob === "**";
|
||||
// GitHub treats "*", "**", and "**/*" as matching every file.
|
||||
return glob === "*" || glob === "**" || glob === "**/*";
|
||||
}
|
||||
|
||||
function describeInstructionRule(globs: string[] | undefined): string {
|
||||
|
||||
@@ -165,6 +165,24 @@ describe("github discovery — Copilot user-global surface", () => {
|
||||
expect(resource.content.trim()).toBe("C# body");
|
||||
});
|
||||
|
||||
test("splits comma-separated applyTo globs and treats **/* as always-apply (#2731)", async () => {
|
||||
write(
|
||||
path.join(cwd, ".github", "instructions", "ts.instructions.md"),
|
||||
"---\napplyTo: '**/*.ts,**/*.tsx'\n---\nTS body\n",
|
||||
);
|
||||
write(path.join(cwd, ".github", "instructions", "all.instructions.md"), "---\napplyTo: '**/*'\n---\nAll body\n");
|
||||
|
||||
const result = await loadCapability<Rule>("rules", { cwd, providers: ["github"] });
|
||||
|
||||
const ts = result.items.find(rule => rule.name === "ts");
|
||||
expect(ts?.alwaysApply).toBe(false);
|
||||
expect(ts?.globs).toEqual(["**/*.ts", "**/*.tsx"]);
|
||||
|
||||
const all = result.items.find(rule => rule.name === "all");
|
||||
expect(all?.alwaysApply).toBe(true);
|
||||
expect(all?.globs).toBeUndefined();
|
||||
});
|
||||
|
||||
test("disabled github provider suppresses copilot instructions and instruction-file rules (#2731)", async () => {
|
||||
write(path.join(cwd, ".github", "copilot-instructions.md"), "project guidance");
|
||||
write(
|
||||
|
||||
Reference in New Issue
Block a user