diff --git a/apps/server/src/main/kotlin/com/correx/apps/server/memory/RepoMapIndexer.kt b/apps/server/src/main/kotlin/com/correx/apps/server/memory/RepoMapIndexer.kt index 61ac841f..a85a9f99 100644 --- a/apps/server/src/main/kotlin/com/correx/apps/server/memory/RepoMapIndexer.kt +++ b/apps/server/src/main/kotlin/com/correx/apps/server/memory/RepoMapIndexer.kt @@ -84,6 +84,12 @@ class RepoMapIndexer( "ts" to Regex("""(?m)^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?(?:function|class|interface|type|enum)\s+(?[A-Za-z_$][A-Za-z0-9_$]*)"""), // GDScript: column-0 declarations only, so indented inner-class members never match. "gd" to Regex("""(?m)^(?:static\s+)?(?:func|class_name|class|signal|enum)\s+(?[A-Za-z_][A-Za-z0-9_]*)"""), + // C# / Godot Mono: top-level type declarations with leading modifiers, kept conservative + // (no method capture — return-type heuristics there are noisy and risk false positives). + "cs" to Regex( + """(?m)^\s*(?:public |private |protected |internal |static |sealed |abstract |""" + + """partial )*(?:class|interface|struct|enum|record)\s+(?[A-Za-z_][A-Za-z0-9_]*)""", + ), // Markdown: h1–h3 headings as "symbols" — deeper levels are noise relative to retrieval value. "md" to Regex("""(?m)^#{1,3}\s+(?.+?)\s*$"""), ) diff --git a/apps/server/src/test/kotlin/com/correx/apps/server/memory/RepoMapIndexerTest.kt b/apps/server/src/test/kotlin/com/correx/apps/server/memory/RepoMapIndexerTest.kt index f609b72b..9d3fd7a8 100644 --- a/apps/server/src/test/kotlin/com/correx/apps/server/memory/RepoMapIndexerTest.kt +++ b/apps/server/src/test/kotlin/com/correx/apps/server/memory/RepoMapIndexerTest.kt @@ -75,6 +75,42 @@ class RepoMapIndexerTest { assertFalse(entry.symbols.contains("help"), "indented inner method must not be matched") } + @Test + fun `extracts C# (Godot Mono) top-level type symbols`(@TempDir root: Path) { + root.resolve("Player.cs").writeText( + """ + using Godot; + + namespace Game; + + public sealed partial class Player : CharacterBody2D + { + private int _health = 100; + + public void TakeDamage(int amount) + { + _health -= amount; + } + } + + internal interface IDamageable + { + void TakeDamage(int amount); + } + + public enum State { Idle, Running } + """.trimIndent(), + ) + + val entry = RepoMapIndexer().index(root).single() + assertEquals("Player.cs", entry.path) + assertTrue( + entry.symbols.containsAll(listOf("Player", "IDamageable", "State")), + "missing symbols in: ${entry.symbols}", + ) + assertFalse(entry.symbols.contains("TakeDamage"), "method/field bodies must not be matched") + } + @Test fun `extracts markdown headings as symbols`(@TempDir root: Path) { root.resolve("design.md").writeText(