diff --git a/actions/setup/js/extract_inline_skills.test.cjs b/actions/setup/js/extract_inline_skills.test.cjs index 13eff8a0a8b..e38466cd275 100644 --- a/actions/setup/js/extract_inline_skills.test.cjs +++ b/actions/setup/js/extract_inline_skills.test.cjs @@ -103,6 +103,15 @@ describe("extractInlineSkills", () => { expect(skills[0].name).toBe("only"); }); + it("uses implicit EOF boundary without explicit end marker", () => { + const content = ["Main.", "", skillMarker("reporting"), "EOF-terminated content."].join("\n"); + const { mainContent, skills } = extractInlineSkills(content); + expect(mainContent).toBe("Main."); + expect(skills).toHaveLength(1); + expect(skills[0].name).toBe("reporting"); + expect(skills[0].content).toBe("EOF-terminated content."); + }); + it("skill content is trimmed", () => { const content = "Main.\n\n" + skillMarker("a") + "\n\n\n Trimmed. \n\n"; const { skills } = extractInlineSkills(content); diff --git a/actions/setup/js/runtime_import.test.cjs b/actions/setup/js/runtime_import.test.cjs index 99ceb5fc467..c873fd3fcbd 100644 --- a/actions/setup/js/runtime_import.test.cjs +++ b/actions/setup/js/runtime_import.test.cjs @@ -20,6 +20,7 @@ const { extractAndReplacePlaceholders, generatePlaceholderName, } = require("./runtime_import.cjs"); +const { extractInlineSkills } = require("./extract_inline_skills.cjs"); describe("runtime_import", () => { let tempDir; let githubDir; @@ -1126,10 +1127,12 @@ describe("runtime_import", () => { fs.writeFileSync(path.join(workflowsDir, "reporting.md"), "## skill: `reporting`\n\nFormatting guidelines.\n"); fs.writeFileSync(path.join(workflowsDir, "otlp.md"), "## Telemetry\n\nOTLP guidance.\n"); const result = await processRuntimeImports("{{#runtime-import reporting.md}}\n{{#runtime-import otlp.md}}\nMain body content.", tempDir); - // The skill block must be explicitly closed before the next import's - // content, otherwise it would swallow "## Telemetry" and everything - // after it since there is no other H2 heading to stop at. expect(result).toContain("## end skill: `reporting`"); + const { mainContent, skills } = extractInlineSkills(result); + expect(skills).toHaveLength(1); + expect(skills[0].name).toBe("reporting"); + expect(skills[0].content).not.toContain("## Telemetry"); + expect(mainContent).toContain("## Telemetry"); expect(result).toContain("## Telemetry"); expect(result).toContain("Main body content."); }), diff --git a/pkg/gitutil/gitutil_test.go b/pkg/gitutil/gitutil_test.go index 2c2a834b9dc..2dc1a532f2e 100644 --- a/pkg/gitutil/gitutil_test.go +++ b/pkg/gitutil/gitutil_test.go @@ -235,7 +235,6 @@ func TestExtractBaseRepo(t *testing.T) { } func TestGetwd(t *testing.T) { - t.Parallel() t.Run("returns the current working directory", func(t *testing.T) { t.Parallel() dir, err := Getwd() diff --git a/pkg/parser/inline_skill_extractor_test.go b/pkg/parser/inline_skill_extractor_test.go index acfda60f6a2..7832e1f1a4f 100644 --- a/pkg/parser/inline_skill_extractor_test.go +++ b/pkg/parser/inline_skill_extractor_test.go @@ -109,6 +109,20 @@ func TestExtractInlineSkills_SkillWithoutFrontmatter(t *testing.T) { assert.Equal(t, "Just a prompt, no frontmatter.", skills[0].Content, "skill content should be the prompt") } +func TestExtractInlineSkills_ImplicitEOFBoundaryWithoutTrailingNewline(t *testing.T) { + // EOF should implicitly terminate the skill block even when the file has no + // trailing newline and no explicit end marker. + markdown := "Main.\n\n" + skillLine("reporting") + "\nEOF-terminated content." + + mainMarkdown, skills, err := ExtractInlineSkills(markdown) + + require.NoError(t, err, "implicit EOF boundary should parse without error") + require.Len(t, skills, 1) + assert.Equal(t, "reporting", skills[0].Name) + assert.Equal(t, "EOF-terminated content.", skills[0].Content) + assert.Equal(t, "Main.", mainMarkdown) +} + func TestExtractInlineSkills_SeparatorWithTrailingWhitespace(t *testing.T) { // Trailing whitespace after the closing backtick should be tolerated markdown := "Main.\n\n" + skillLine("padded") + " \nSkill content."