e1d774ff
feat(scanner): tagged marker reporting, size cap, binary sniff
a73x 2026-04-29 05:46
Commit message
internal/scanner/scanner.go
| Old | New | ||
|---|---|---|---|
| @@ -2,6 +2,7 @@ package scanner | |||
| 2 | 2 | ||
| 3 | import ( | 3 | import ( |
| 4 | "bufio" | 4 | "bufio" |
| 5 | "bytes" | ||
| 5 | "os" | 6 | "os" |
| 6 | "path/filepath" | 7 | "path/filepath" |
| 7 | "regexp" | 8 | "regexp" |
| @@ -58,6 +59,32 @@ func Scan(path string) ([]Marker, error) { | |||
| 58 | return nil, nil | 59 | return nil, nil |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 62 | const maxBytes = 1 << 20 // 1 MB | ||
| 63 | |||
| 64 | st, err := os.Stat(path) | ||
| 65 | if err != nil { | ||
| 66 | return nil, err | ||
| 67 | } | ||
| 68 | if st.Size() > maxBytes { | ||
| 69 | return nil, nil | ||
| 70 | } | ||
| 71 | |||
| 72 | // Binary sniff: read first 8 KB, look for null byte. | ||
| 73 | sniffN := int64(8192) | ||
| 74 | if st.Size() < sniffN { | ||
| 75 | sniffN = st.Size() | ||
| 76 | } | ||
| 77 | sniff := make([]byte, sniffN) | ||
| 78 | sf, err := os.Open(path) | ||
| 79 | if err != nil { | ||
| 80 | return nil, err | ||
| 81 | } | ||
| 82 | n, _ := sf.Read(sniff) | ||
| 83 | sf.Close() | ||
| 84 | if bytes.IndexByte(sniff[:n], 0) >= 0 { | ||
| 85 | return nil, nil | ||
| 86 | } | ||
| 87 | |||
| 61 | f, err := os.Open(path) | 88 | f, err := os.Open(path) |
| 62 | if err != nil { | 89 | if err != nil { |
| 63 | return nil, err | 90 | return nil, err |
internal/scanner/scanner_test.go
| Old | New | ||
|---|---|---|---|
| @@ -3,6 +3,7 @@ package scanner | |||
| 3 | import ( | 3 | import ( |
| 4 | "os" | 4 | "os" |
| 5 | "path/filepath" | 5 | "path/filepath" |
| 6 | "strings" | ||
| 6 | "testing" | 7 | "testing" |
| 7 | ) | 8 | ) |
| 8 | 9 | ||
| @@ -109,3 +110,46 @@ func TestScanMakefileByName(t *testing.T) { | |||
| 109 | t.Fatalf("got %#v", markers) | 110 | t.Fatalf("got %#v", markers) |
| 110 | } | 111 | } |
| 111 | } | 112 | } |
| 113 | |||
| 114 | func TestScanTaggedMarkerNotFiredButReported(t *testing.T) { | ||
| 115 | dir := t.TempDir() | ||
| 116 | path := writeFile(t, dir, "foo.go", "// @claude[fw-a1b2c3d4] write a test\n") | ||
| 117 | markers, err := Scan(path) | ||
| 118 | if err != nil { | ||
| 119 | t.Fatal(err) | ||
| 120 | } | ||
| 121 | if len(markers) != 1 { | ||
| 122 | t.Fatalf("want 1 marker, got %d", len(markers)) | ||
| 123 | } | ||
| 124 | if !markers[0].Tagged || markers[0].UUID != "fw-a1b2c3d4" { | ||
| 125 | t.Errorf("got %#v, want Tagged=true UUID=fw-a1b2c3d4", markers[0]) | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | func TestScanLargeFileSkipped(t *testing.T) { | ||
| 130 | dir := t.TempDir() | ||
| 131 | big := strings.Repeat("// filler line\n", 80_000) // > 1 MB | ||
| 132 | path := writeFile(t, dir, "big.go", big+"// @claude do it\n") | ||
| 133 | markers, err := Scan(path) | ||
| 134 | if err != nil { | ||
| 135 | t.Fatal(err) | ||
| 136 | } | ||
| 137 | if markers != nil { | ||
| 138 | t.Fatalf("expected nil for oversized file, got %#v", markers) | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | func TestScanBinaryFileSkipped(t *testing.T) { | ||
| 143 | dir := t.TempDir() | ||
| 144 | path := filepath.Join(dir, "blob.go") | ||
| 145 | if err := os.WriteFile(path, []byte{0x00, 0x01, 0x02, '/', '/', ' ', '@', 'c', 'l', 'a', 'u', 'd', 'e', ' ', 'x'}, 0o644); err != nil { | ||
| 146 | t.Fatal(err) | ||
| 147 | } | ||
| 148 | markers, err := Scan(path) | ||
| 149 | if err != nil { | ||
| 150 | t.Fatal(err) | ||
| 151 | } | ||
| 152 | if markers != nil { | ||
| 153 | t.Fatalf("expected nil for binary file, got %#v", markers) | ||
| 154 | } | ||
| 155 | } | ||