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
20 changes: 12 additions & 8 deletions .agents/skills/go-engineer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ lives in `.agents/skills/review-docs/SKILL.md`.
compatibility constraints, or verification target is not explicit.
3. Apply the MUST / MUST NOT rules while editing.
4. Defer test structure and fixtures to `go-tester` for test changes.
5. Verify with the narrowest relevant Go test first, then the repository-level
checks listed below.
5. Verify with the narrowest relevant Go test and lint target first, then the
repository-level checks listed below.
6. Follow the git-history policy in `AGENTS.md`.

## Setup Check
Expand All @@ -47,9 +47,10 @@ Run this before non-trivial Go changes or when the package baseline is unclear:
support package.
3. **Test owner** - identify the package test suite and fixtures that already
cover the behavior.
4. **Commands** - plan `gofmt`, focused `go test`, `go vet ./...`, full
`go test ./...`, and `go build -trimpath main.go` when integration or CLI
behavior changes.
4. **Commands** - plan `gofmt`, focused `go test`, focused
`golangci-lint run`, `go vet ./...`, full `go test ./...`,
`golangci-lint run ./...`, and `go build -trimpath main.go` when
integration or CLI behavior changes.

## Processing Flow

Expand Down Expand Up @@ -150,9 +151,12 @@ Run the narrowest relevant checks first, then broaden:

1. `gofmt -w <changed-go-files>`
2. `go test ./<affected-package>`
3. `go vet ./...`
4. `go test ./...`
5. `go build -trimpath main.go` when CLI, configuration, embedding, or package
3. `golangci-lint run ./<affected-package>` or the smallest package set that
contains touched Go files
4. `go vet ./...`
5. `go test ./...`
6. `golangci-lint run ./...`
7. `go build -trimpath main.go` when CLI, configuration, embedding, or package
integration behavior changed

For user-visible CLI behavior, run a focused `go run ./main.go ...` scenario
Expand Down
8 changes: 6 additions & 2 deletions .agents/skills/go-tester/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ Two companions own neighboring concerns:
fixture needed to express the behavior.
5. **Assert behavior, not implementation trivia.** Prefer outputs, changed-file
lists, typed errors, and line numbers over private state.
6. **Run the narrowest test first.** Broaden only after the focused test passes.
6. **Run the narrowest test and lint target first.** Broaden only after the
focused package passes.

## Naming And Structure

Expand Down Expand Up @@ -112,7 +113,10 @@ After Go test code changes, run:

1. `gofmt -w <changed-go-files>`
2. the focused package test
3. `go test ./...`
3. `golangci-lint run ./<affected-package>` or the smallest package set that
contains touched Go files
4. `go test ./...`
5. `golangci-lint run ./...`

Run `go vet ./...` when the test change also changes helper code, exported
test support, or production code.
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,10 @@ jobs:
- name: Run Tests
run: go test -v ./...

- name: Run Fuzz Tests
run: |
go test -run=^$ -fuzz=FuzzCutIndent -fuzztime=5s ./indent
go test -run=^$ -fuzz=FuzzPatternFindIn -fuzztime=5s ./embedding/parsing

- name: Run E2E Tests
run: go test -v -tags showcase ./showcase
35 changes: 35 additions & 0 deletions embedding/parsing/instruction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ package parsing_test

import (
_type "embed-code/embed-code-go/type"
"errors"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -136,6 +137,19 @@ var _ = Describe("Instruction", func() {
Expect(err).Should(MatchError(ContainSubstring("invalid start pattern `[`")))
})

It("should embed a line ending with a literal opening brace pattern", func() {
instructionParams := TestInstructionParams{
lineGlob: "class Hello {",
}

actualLines := getXMLExtractionContent(
"org/example/Hello.java", instructionParams, config)

Expect(actualLines).Should(Equal([]string{
"public class Hello {",
}))
})

It("should successfully read source content", func() {
instructionParams := TestInstructionParams{
closeTag: true,
Expand Down Expand Up @@ -551,6 +565,27 @@ var _ = Describe("Instruction", func() {
))
})

It("should report pattern not found when the glob matcher panics", func() {
sourceRoot := GinkgoT().TempDir()
config.CodeRoots = _type.NamedPathList{_type.NamedPath{Path: sourceRoot}}
Expect(os.WriteFile(
filepath.Join(sourceRoot, "panic-pattern.txt"),
[]byte("0\n"),
0600,
)).To(Succeed())
xmlString := buildInstruction("panic-pattern.txt", TestInstructionParams{
lineGlob: "0{}",
})
instruction := createInstructionFromXML(xmlString, config)

_, err := instruction.Content()

var patternErr parsing.PatternNotFoundError
Expect(errors.As(err, &patternErr)).Should(BeTrue())
Expect(patternErr.Kind).Should(Equal("line"))
Expect(err).Should(MatchError(ContainSubstring("line pattern `0{}`")))
})

It("should use shared resolver as default", func() {
sourceRoot := GinkgoT().TempDir()
markdownPath := filepath.Join(GinkgoT().TempDir(), "doc.md")
Expand Down
53 changes: 48 additions & 5 deletions embedding/parsing/pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,60 @@ func compileLineMatcher(patternLine string) (lineMatcher, error) {
pattern = pattern[:lastIndex]
}

compiledGlob, err := glob.Compile(pattern)
if err != nil {
return lineMatcher{}, err
var matcher lineMatcher
var compileErr error
func() {
defer func() {
recovered := recover()
if recovered != nil {
compileErr = fmt.Errorf("glob pattern compiler panicked: %v", recovered)
}
}()

compiledGlob, err := glob.Compile(pattern)
if err != nil {
compileErr = err

return
}
matcher = lineMatcher{compiled: compiledGlob}
}()
if compileErr != nil {
return lineMatcher{}, compileErr
}

return lineMatcher{compiled: compiledGlob}, nil
return matcher, nil
}

// matches reports whether the source line matches the compiled pattern.
func (m lineMatcher) matches(line string) bool {
return m.compiled != nil && m.compiled.Match(line)
if m.compiled == nil {
return false
}

return matchGlob(m.compiled, line)
}

// matchGlob reports whether a glob matches and treats dependency panics as misses.
//
// A miss lets the instruction layer report PatternNotFoundError with source
// context instead of exposing a third-party matcher panic to users.
// Recovery stays at single-line matcher granularity so one panicking candidate
// does not abort the rest of the source scan.
func matchGlob(compiledGlob glob.Glob, line string) bool {
var matched bool
func() {
defer func() {
recovered := recover()
if recovered != nil {
matched = false
}
}()

matched = compiledGlob.Match(line)
}()

return matched
}

// FindIn returns the first source-line range matching the pattern.
Expand Down
83 changes: 83 additions & 0 deletions embedding/parsing/pattern_fuzz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2026, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package parsing_test

import (
"strings"
"testing"

"embed-code/embed-code-go/embedding/parsing"
)

// FuzzPatternFindIn checks source-line pattern compilation and matching.
func FuzzPatternFindIn(f *testing.F) {
f.Add("main", "public static void main(String[] args) {\n return;\n}", 0)
f.Add("^ padded text $ \\n ^Use \\* to multiply$", " padded text \nUse * to multiply", 0)
f.Add("^Use \\* to multiply$ \\n żółć$", "Use * to multiply\nżółć", 0)
f.Add(" return;", " System.out.println(\"Hi\");\n \n return;", 0)
f.Add("[", "invalid pattern is rejected", 0)
f.Add("0{$", "0", 0)
f.Add("0{}", "0", 0)

f.Fuzz(func(t *testing.T, sourceGlob string, source string, startFrom int) {
if len(sourceGlob) > 4096 || len(source) > 8192 {
t.Skip("generated input exceeds the bounded fuzz target size")
}

pattern, err := parsing.NewPattern(sourceGlob)
if err != nil {
return
}
lines := strings.Split(source, "\n")

start, end, found := pattern.FindIn(lines, startFrom)

if !found {
return
}
assertValidPatternMatch(t, lines, startFrom, start, end)
})
}

// assertValidPatternMatch checks the public range contract returned by FindIn.
func assertValidPatternMatch(t *testing.T, lines []string, startFrom int, start int, end int) {
t.Helper()
if start < 0 || start >= len(lines) {
t.Fatalf("start index %d is outside %d source lines", start, len(lines))
}
if end < start || end >= len(lines) {
t.Fatalf(
"end index %d is outside matched range starting at %d in %d source lines",
end,
start,
len(lines),
)
}
if startFrom >= 0 && start < startFrom {
t.Fatalf("match starts at %d before requested start %d", start, startFrom)
}
}
70 changes: 70 additions & 0 deletions indent/indent_fuzz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2026, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package indent_test

import (
"strings"
"testing"

"embed-code/embed-code-go/indent"
)

// FuzzCutIndent checks indentation trimming over arbitrary source blocks.
func FuzzCutIndent(f *testing.F) {
f.Add(" System.out.println(\"Hi\");\n \n return;")
f.Add("\n foo\n bar\n\n baz")
f.Add("\t\tfirst\n\tsecond\n")
f.Add("plain\ntext")

f.Fuzz(func(t *testing.T, source string) {
lines := strings.Split(source, "\n")
commonIndent := indent.MaxCommonIndentation(lines)

changedLines := indent.CutIndent(lines, commonIndent)

if len(changedLines) != len(lines) {
t.Fatalf("CutIndent returned %d lines for %d input lines", len(changedLines), len(lines))
}
for lineIndex, line := range lines {
if commonIndent <= len(line) && changedLines[lineIndex] != line[commonIndent:] {
t.Fatalf(
"line %d was cut incorrectly: got %q, want %q",
lineIndex,
changedLines[lineIndex],
line[commonIndent:],
)
}
if commonIndent > len(line) && changedLines[lineIndex] != "" {
t.Fatalf(
"short line %d was not fully removed: got %q",
lineIndex,
changedLines[lineIndex],
)
}
}
})
}
5 changes: 5 additions & 0 deletions showcase/embedding/positive/instruction-tag.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ configured `code-path`. With named source roots, start the value with `$name/`,
such as `$java/org/showcase/Greeting.java`. With one unnamed source root, use a
path relative to that root.

Embed-code treats documentation files and embedding instructions as trusted
local input. The `file` value may intentionally use relative path segments such
as `../`; embed-code resolves the path against the selected source root but does
not enforce that the final path stays inside that root.

Use one source-selection shape per instruction:

- `file` alone embeds the whole source file.
Expand Down
Loading