fix(coding-agent): unify source provenance, closes #1734

This commit is contained in:
Mario Zechner
2026-03-23 02:02:42 +01:00
parent 883862a354
commit 4e5af01d73
26 changed files with 340 additions and 157 deletions

View File

@@ -3,10 +3,29 @@ import { join, resolve } from "path";
import { describe, expect, it } from "vitest";
import type { ResourceDiagnostic } from "../src/core/diagnostics.js";
import { formatSkillsForPrompt, loadSkills, loadSkillsFromDir, type Skill } from "../src/core/skills.js";
import { createSyntheticSourceInfo } from "../src/core/source-info.js";
const fixturesDir = resolve(__dirname, "fixtures/skills");
const collisionFixturesDir = resolve(__dirname, "fixtures/skills-collision");
function createTestSkill(options: {
name: string;
description: string;
filePath: string;
baseDir: string;
disableModelInvocation?: boolean;
source?: string;
}): Skill {
return {
name: options.name,
description: options.description,
filePath: options.filePath,
baseDir: options.baseDir,
sourceInfo: createSyntheticSourceInfo(options.filePath, { source: options.source ?? "test" }),
disableModelInvocation: options.disableModelInvocation ?? false,
};
}
describe("skills", () => {
describe("loadSkillsFromDir", () => {
it("should load a valid skill", () => {
@@ -18,7 +37,7 @@ describe("skills", () => {
expect(skills).toHaveLength(1);
expect(skills[0].name).toBe("valid-skill");
expect(skills[0].description).toBe("A valid skill for testing purposes.");
expect(skills[0].source).toBe("test");
expect(skills[0].sourceInfo.source).toBe("test");
expect(diagnostics).toHaveLength(0);
});
@@ -210,14 +229,12 @@ describe("skills", () => {
it("should format skills as XML", () => {
const skills: Skill[] = [
{
createTestSkill({
name: "test-skill",
description: "A test skill.",
filePath: "/path/to/skill/SKILL.md",
baseDir: "/path/to/skill",
source: "test",
disableModelInvocation: false,
},
}),
];
const result = formatSkillsForPrompt(skills);
@@ -232,14 +249,12 @@ describe("skills", () => {
it("should include intro text before XML", () => {
const skills: Skill[] = [
{
createTestSkill({
name: "test-skill",
description: "A test skill.",
filePath: "/path/to/skill/SKILL.md",
baseDir: "/path/to/skill",
source: "test",
disableModelInvocation: false,
},
}),
];
const result = formatSkillsForPrompt(skills);
@@ -252,14 +267,12 @@ describe("skills", () => {
it("should escape XML special characters", () => {
const skills: Skill[] = [
{
createTestSkill({
name: "test-skill",
description: 'A skill with <special> & "characters".',
filePath: "/path/to/skill/SKILL.md",
baseDir: "/path/to/skill",
source: "test",
disableModelInvocation: false,
},
}),
];
const result = formatSkillsForPrompt(skills);
@@ -271,22 +284,18 @@ describe("skills", () => {
it("should format multiple skills", () => {
const skills: Skill[] = [
{
createTestSkill({
name: "skill-one",
description: "First skill.",
filePath: "/path/one/SKILL.md",
baseDir: "/path/one",
source: "test",
disableModelInvocation: false,
},
{
}),
createTestSkill({
name: "skill-two",
description: "Second skill.",
filePath: "/path/two/SKILL.md",
baseDir: "/path/two",
source: "test",
disableModelInvocation: false,
},
}),
];
const result = formatSkillsForPrompt(skills);
@@ -298,22 +307,19 @@ describe("skills", () => {
it("should exclude skills with disableModelInvocation from prompt", () => {
const skills: Skill[] = [
{
createTestSkill({
name: "visible-skill",
description: "A visible skill.",
filePath: "/path/visible/SKILL.md",
baseDir: "/path/visible",
source: "test",
disableModelInvocation: false,
},
{
}),
createTestSkill({
name: "hidden-skill",
description: "A hidden skill.",
filePath: "/path/hidden/SKILL.md",
baseDir: "/path/hidden",
source: "test",
disableModelInvocation: true,
},
}),
];
const result = formatSkillsForPrompt(skills);
@@ -325,14 +331,13 @@ describe("skills", () => {
it("should return empty string when all skills have disableModelInvocation", () => {
const skills: Skill[] = [
{
createTestSkill({
name: "hidden-skill",
description: "A hidden skill.",
filePath: "/path/hidden/SKILL.md",
baseDir: "/path/hidden",
source: "test",
disableModelInvocation: true,
},
}),
];
const result = formatSkillsForPrompt(skills);
@@ -351,7 +356,7 @@ describe("skills", () => {
skillPaths: [join(fixturesDir, "valid-skill")],
});
expect(skills).toHaveLength(1);
expect(skills[0].source).toBe("path");
expect(skills[0].sourceInfo.scope).toBe("temporary");
expect(diagnostics).toHaveLength(0);
});
@@ -415,7 +420,7 @@ describe("skills", () => {
}
expect(skillMap.size).toBe(1);
expect(skillMap.get("calendar")?.source).toBe("first");
expect(skillMap.get("calendar")?.sourceInfo.source).toBe("first");
expect(collisionWarnings).toHaveLength(1);
expect(collisionWarnings[0].message).toContain("name collision");
});