david před 1 dnem
revize
75041e1398

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+/target
+AGENTS.md
+codebase_summary.md

+ 638 - 0
Cargo.lock

@@ -0,0 +1,638 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
+
+[[package]]
+name = "anstyle-parse"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+
+[[package]]
+name = "bitflags"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "clap"
+version = "4.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
+
+[[package]]
+name = "codebase_summarizer"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "regex",
+ "tempfile",
+ "walkdir",
+]
+
+[[package]]
+name = "colorchoice"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "getrandom"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasip2",
+ "wasip3",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "foldhash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "id-arena"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
+
+[[package]]
+name = "indexmap"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.17.0",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
+
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "leb128fmt"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+
+[[package]]
+name = "libc"
+version = "0.2.186"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "memchr"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
+name = "regex"
+version = "1.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
+
+[[package]]
+name = "rustix"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
+dependencies = [
+ "fastrand",
+ "getrandom",
+ "once_cell",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasip2"
+version = "1.0.3+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
+dependencies = [
+ "wit-bindgen 0.57.1",
+]
+
+[[package]]
+name = "wasip3"
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
+dependencies = [
+ "wit-bindgen 0.51.0",
+]
+
+[[package]]
+name = "wasm-encoder"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
+dependencies = [
+ "leb128fmt",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
+dependencies = [
+ "bitflags",
+ "hashbrown 0.15.5",
+ "indexmap",
+ "semver",
+]
+
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+dependencies = [
+ "wit-bindgen-rust-macro",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.57.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
+
+[[package]]
+name = "wit-bindgen-core"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
+dependencies = [
+ "anyhow",
+ "heck",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-bindgen-rust"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
+dependencies = [
+ "anyhow",
+ "heck",
+ "indexmap",
+ "prettyplease",
+ "syn",
+ "wasm-metadata",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
+[[package]]
+name = "wit-bindgen-rust-macro"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
+dependencies = [
+ "anyhow",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wit-bindgen-core",
+ "wit-bindgen-rust",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
+dependencies = [
+ "anyhow",
+ "bitflags",
+ "indexmap",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

+ 21 - 0
Cargo.toml

@@ -0,0 +1,21 @@
+[package]
+name = "codebase_summarizer"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "codebase_summarizer"
+path = "src/lib.rs"
+
+[[bin]]
+name = "codebase_summarizer"
+path = "src/main.rs"
+
+[dependencies]
+clap = { version = "4.5", features = ["derive"] }
+walkdir = "2.5"
+regex = "1.10"
+anyhow = "1.0"
+
+[dev-dependencies]
+tempfile = "3.10"

+ 55 - 0
README.md

@@ -0,0 +1,55 @@
+# Codebase Summarizer
+
+A command-line tool that extracts a concise summary of a codebase, including symbols and their documentation comments, generating a `codebase_summary.md` file.
+
+## Why?
+
+LLMs charge per-token. When you feed them an entire codebase, you're paying for lines that don't matter. This tool reduces context size by extracting only the essential structure: symbols, their types, and their documentation. The result is a lean summary that lets you understand any codebase in seconds—without the cost of feeding it everything.
+
+## Features
+
+- **Multi-language support**: Go, Rust, Python, Dart, R, TypeScript/JavaScript, Java, C, C++, C#, Ruby, PHP, Swift, Kotlin, and more
+- **Symbol extraction**: Functions, structs, enums, classes, methods, traits, interfaces, type aliases
+- **Doc comment preservation**: Captures documentation comments associated with each symbol
+- **Markdown output**: Clean `codebase_summary.md` documenting the entire codebase
+- **AI agent optimized**: Generates `AGENTS.md` with navigation instructions for AI agents
+
+## Installation
+
+### Build from source
+
+```bash
+git clone gogs.dmsc.dev/dmsc/codebase_summarizer.git
+cd codebase_summarizer
+cargo build --release
+sudo cp target/release/codebase_summarizer /usr/local/bin/
+```
+
+## Usage
+
+```bash
+# Scan current directory
+codebase_summarizer
+
+# Scan a specific directory
+codebase_summarizer --directory /path/to/codebase
+
+# Output to custom location
+codebase_summarizer --output /tmp/summary.md
+
+# Skip AGENTS.md generation
+codebase_summarizer --no-agents
+
+# Enable verbose output
+codebase_summarizer --verbose
+
+# Include private symbols
+codebase_summarizer --include-all
+```
+
+## How it works
+
+1. **Scan**: Recursively walks the directory, filtering out `target/`, `node_modules/`, `.git/`, and other non-source directories
+2. **Parse**: Extracts symbols from each code file using language-specific parsers
+3. **Summarize**: Generates a `codebase_summary.md` with file tree and symbol documentation
+4. **Optional**: Creates `AGENTS.md` with navigation protocol for AI agents

+ 31 - 0
spec/main.md

@@ -0,0 +1,31 @@
+codebase_summarizer is a command-line tool written in Rust that recursively traverses a given directory or the current working directory if none is given. It identifies code files and extracts a concise one-file summary of the codebase enriched with DOC-strings/comments. 
+
+The outputs of this tool are:
+* codebase_summary.md, a markdown file like this example
+```
+# Codebase Architecture Map
+
+## File: pkg/auth/service.go
+### func (s *AuthService) Login(user, pass string) (*Token, error)
+**Doc:** 
+// Login validates the user credentials against the database.
+// Returns a JWT token if successful or an error if unauthorized.
+
+## File: cmd/api/main.go
+### type Config struct
+**Doc:** 
+// Config holds the application environment settings.
+```
+* agents.md, a read me type file for agents if no such file exists yet. This file instructs the coding agents how to use the codebase_summary
+```
+# Codebase Navigation Protocol
+
+You have access to codebase_summary.md. This file contains a compressed map of the codebase, including exported signatures and their associated documentation comments.
+
+Protocol:
+
+Consult Summary First: Use the doc-strings in codebase_summary.md to understand the intent and usage of functions without reading the full implementation.
+Identify Targets: Identify the specific files containing the logic you need.
+Deep Dive: Open the identified files to study the internal logic/edge cases before proposing a solution.
+After making changes: run the codebase_summarizer tool to update the codebase_summary.md again if necessary
+```

+ 6 - 0
src/lib.rs

@@ -0,0 +1,6 @@
+//! Codebase Summarizer Library
+
+pub mod models;
+pub mod parser;
+pub mod scanner;
+pub mod writer;

+ 139 - 0
src/main.rs

@@ -0,0 +1,139 @@
+//! Codebase Summarizer CLI
+//! 
+//! A command-line tool that recursively traverses a directory and extracts
+//! a concise summary of the codebase enriched with doc-strings/comments.
+
+use std::path::PathBuf;
+use anyhow::{Context, Result};
+use clap::Parser;
+
+mod models;
+mod scanner;
+mod parser;
+mod writer;
+
+use models::CodebaseSummary;
+use scanner::{Scanner, read_file};
+use parser::parse_file;
+use writer::{MarkdownWriter, AGENTS_INSTRUCTIONS};
+
+/// Codebase Summarizer - Extracts code summaries with doc-strings
+#[derive(Parser, Debug)]
+#[command(name = "codebase_summarizer")]
+#[command(version = "0.1.0")]
+#[command(about = "Recursively traverses a directory and extracts a concise summary of the codebase enriched with doc-strings/comments.")]
+struct Args {
+    /// The directory to scan. Defaults to the current working directory.
+    #[arg(default_value = ".")]
+    directory: String,
+    
+    /// Output file path for the summary. Defaults to codebase_summary.md in the scanned directory.
+    #[arg(short, long)]
+    output: Option<String>,
+    
+    /// Skip generating AGENTS.md file.
+    #[arg(short, long, default_value = "false")]
+    no_agents: bool,
+    
+    /// Include files without doc comments.
+    #[arg(short, long, default_value = "false")]
+    include_all: bool,
+    
+    /// Verbose output.
+    #[arg(short, long, default_value = "false")]
+    verbose: bool,
+
+    /// Print the agent instructions to stdout and exit.
+    #[arg(long)]
+    print_instructions: bool,
+}
+
+fn main() -> Result<()> {
+    let args = Args::parse();
+    
+    if args.print_instructions {
+        println!("{}", AGENTS_INSTRUCTIONS);
+        return Ok(());
+    }
+    
+    // Determine the root directory
+    let root = if args.directory == "." {
+        std::env::current_dir()
+            .context("Failed to get current working directory")?
+    } else {
+        PathBuf::from(&args.directory)
+    };
+    
+    if args.verbose {
+        eprintln!("Scanning directory: {}", root.display());
+    }
+    
+    // Create scanner and find all code files
+    let scanner = Scanner::default_config();
+    let files = scanner.scan(&root)
+        .context("Failed to scan directory")?;
+    
+    if args.verbose {
+        eprintln!("Found {} code files", files.len());
+    }
+    
+    // Parse each file and collect symbols
+    let mut summary = CodebaseSummary::new(root.clone());
+    
+    for file_path in &files {
+        match read_file(file_path) {
+            Ok(content) => {
+                let file_summary = parse_file(file_path, &content);
+                
+                // Filter out files without symbols unless include_all is set
+                if !file_summary.symbols.is_empty() || args.include_all {
+                    summary.add_file(file_summary);
+                }
+            }
+            Err(e) => {
+                if args.verbose {
+                    eprintln!("Warning: Failed to read {}: {}", file_path.display(), e);
+                }
+            }
+        }
+    }
+    
+    if args.verbose {
+        eprintln!("Extracted {} total symbols", summary.total_symbols());
+    }
+    
+    // Determine output path
+    let output_path = if let Some(output) = args.output {
+        PathBuf::from(output)
+    } else {
+        root.join("codebase_summary.md")
+    };
+    
+    // Write the summary
+    let writer = MarkdownWriter::new();
+    writer.write_summary(&summary, &output_path)
+        .context("Failed to write summary")?;
+    
+    println!("Summary written to: {}", output_path.display());
+    
+    // Write AGENTS.md if not disabled and doesn't exist
+    if !args.no_agents {
+        match writer.write_agents_md(&root) {
+            Ok(true) => {
+                println!("Created AGENTS.md in: {}", root.display());
+            }
+            Ok(false) => {
+                if args.verbose {
+                    println!("AGENTS.md already exists, skipping");
+                }
+            }
+            Err(e) => {
+                if args.verbose {
+                    eprintln!("Warning: Failed to write AGENTS.md: {}", e);
+                }
+            }
+        }
+    }
+    
+    Ok(())
+}

+ 146 - 0
src/models.rs

@@ -0,0 +1,146 @@
+//! Data models for codebase summarizer.
+
+use std::path::PathBuf;
+
+/// Represents a single symbol (function, method, type, etc.) extracted from a file.
+#[derive(Debug, Clone)]
+pub struct Symbol {
+    /// The kind of symbol (function, struct, enum, etc.)
+    pub kind: SymbolKind,
+    /// The name of the symbol.
+    pub name: String,
+    /// The full signature (parameters, return type, etc.).
+    pub signature: String,
+    /// Documentation comment preceding the symbol.
+    pub doc: Option<String>,
+    /// Line number where the symbol is defined.
+    pub line: usize,
+}
+
+/// The kind of symbol.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum SymbolKind {
+    Function,
+    Method,
+    Struct,
+    Enum,
+    Class,
+    Interface,
+    Trait,
+    TypeAlias,
+    Const,
+    Static,
+    Field,
+    Module,
+    Import,
+}
+
+/// Represents a summary of a single code file.
+#[derive(Debug, Clone)]
+pub struct FileSummary {
+    /// The path to the file.
+    pub path: PathBuf,
+    /// The language of the file.
+    pub language: Language,
+    /// The symbols extracted from the file.
+    pub symbols: Vec<Symbol>,
+}
+
+/// Supported programming languages.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Language {
+    Go,
+    Rust,
+    Python,
+    Dart,
+    R,
+    TypeScript,
+    JavaScript,
+    Java,
+    C,
+    Cpp,
+    CSharp,
+    Ruby,
+    Php,
+    Swift,
+    Kotlin,
+    Unknown,
+}
+
+impl Language {
+    /// Get the language from a file extension.
+    pub fn from_extension(ext: &str) -> Self {
+        match ext.to_lowercase().as_str() {
+            "go" => Language::Go,
+            "rs" => Language::Rust,
+            "py" => Language::Python,
+            "dart" => Language::Dart,
+            "r" | "R" => Language::R,
+            "ts" => Language::TypeScript,
+            "tsx" => Language::TypeScript,
+            "js" => Language::JavaScript,
+            "jsx" => Language::JavaScript,
+            "java" => Language::Java,
+            "c" => Language::C,
+            "h" => Language::C,
+            "cpp" | "cc" | "cxx" | "hpp" => Language::Cpp,
+            "cs" => Language::CSharp,
+            "rb" => Language::Ruby,
+            "php" => Language::Php,
+            "swift" => Language::Swift,
+            "kt" => Language::Kotlin,
+            _ => Language::Unknown,
+        }
+    }
+
+    /// Get the file extensions for this language.
+    pub fn extensions(&self) -> Vec<&'static str> {
+        match self {
+            Language::Go => vec!["go"],
+            Language::Rust => vec!["rs"],
+            Language::Python => vec!["py"],
+            Language::Dart => vec!["dart"],
+            Language::R => vec!["r", "R"],
+            Language::TypeScript => vec!["ts", "tsx"],
+            Language::JavaScript => vec!["js", "jsx"],
+            Language::Java => vec!["java"],
+            Language::C => vec!["c", "h"],
+            Language::Cpp => vec!["cpp", "cc", "cxx", "hpp"],
+            Language::CSharp => vec!["cs"],
+            Language::Ruby => vec!["rb"],
+            Language::Php => vec!["php"],
+            Language::Swift => vec!["swift"],
+            Language::Kotlin => vec!["kt"],
+            Language::Unknown => vec![],
+        }
+    }
+}
+
+/// Represents the entire codebase summary.
+#[derive(Debug, Clone)]
+pub struct CodebaseSummary {
+    /// The root directory that was scanned.
+    pub root: PathBuf,
+    /// Summaries for all files.
+    pub files: Vec<FileSummary>,
+}
+
+impl CodebaseSummary {
+    /// Create a new empty summary.
+    pub fn new(root: PathBuf) -> Self {
+        Self {
+            root,
+            files: Vec::new(),
+        }
+    }
+
+    /// Add a file summary.
+    pub fn add_file(&mut self, summary: FileSummary) {
+        self.files.push(summary);
+    }
+
+    /// Get the total number of symbols.
+    pub fn total_symbols(&self) -> usize {
+        self.files.iter().map(|f| f.symbols.len()).sum()
+    }
+}

+ 160 - 0
src/parser/dart.rs

@@ -0,0 +1,160 @@
+//! Dart language parser.
+
+use regex::Regex;
+use std::path::PathBuf;
+
+use crate::models::{FileSummary, Language, Symbol, SymbolKind};
+use super::extract_doc;
+
+/// Parser for Dart source files.
+pub struct DartParser;
+
+impl DartParser {
+    pub fn new() -> Self {
+        Self
+    }
+
+    fn parse_content(&self, content: &str) -> Vec<Symbol> {
+        let mut symbols = Vec::new();
+        let lines: Vec<&str> = content.lines().collect();
+
+        // Function pattern: return_type function_name(...)
+        let func_regex = Regex::new(r"^\w+\s+(\w+)\s*\([^)]*\)\s*(?:\{|=>|;)\)").unwrap();
+        
+        // Void function pattern: void function_name(...)
+        let void_func_regex = Regex::new(r"^void\s+(\w+)\s*\(").unwrap();
+        
+        // Static function pattern: static return_type function_name(...)
+        let static_func_regex = Regex::new(r"^static\s+\w+\s+(\w+)\s*\(").unwrap();
+        
+        // Class pattern: class ClassName
+        let class_regex = Regex::new(r"^class\s+(\w+)").unwrap();
+        
+        // Enum pattern: enum EnumName
+        let enum_regex = Regex::new(r"^enum\s+(\w+)").unwrap();
+        
+        // Interface pattern: abstract class or mixin or extension
+        let interface_regex = Regex::new(r"^(abstract\s+class|mixin|extension|interface)\s+(\w+)").unwrap();
+        
+        // Constructor pattern: ClassName(...)
+        let constructor_regex = Regex::new(r"^\s*(\w+)\s*\([^)]*\)\s*(?:\{|;)").unwrap();
+        
+        // Field pattern: type fieldName;
+        let field_regex = Regex::new(r"^\w+\s+(\w+)\s*(?:=|;|,)").unwrap();
+
+        for (line_num, line) in lines.iter().enumerate() {
+            let trimmed = line.trim();
+            let line_number = line_num + 1;
+
+            // Skip comments and empty lines
+            if trimmed.is_empty() || trimmed.starts_with("//") || trimmed.starts_with("/*") {
+                continue;
+            }
+
+            // Check for void function
+            if let Some(caps) = void_func_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                // Skip main function
+                if name == "main" {
+                    continue;
+                }
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for static function
+            else if let Some(caps) = static_func_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for class
+            else if let Some(caps) = class_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Class,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for enum
+            else if let Some(caps) = enum_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Enum,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for interface/mixin/extension
+            else if let Some(caps) = interface_regex.captures(trimmed) {
+                let name = caps.get(2).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Interface,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+        }
+
+        symbols
+    }
+}
+
+impl super::LanguageParser for DartParser {
+    fn parse(&self, path: &PathBuf, content: &str) -> FileSummary {
+        FileSummary {
+            path: path.clone(),
+            language: Language::Dart,
+            symbols: self.parse_content(content),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::parser::LanguageParser;
+
+    #[test]
+    fn test_parse_dart() {
+        let parser = DartParser::new();
+        let content = r#"
+/// A simple Dart class.
+class UserService {
+  /// Login method.
+  void login(String user, String pass) {
+    // implementation
+  }
+  
+  /// Logout method.
+  void logout() {
+    // implementation
+  }
+}
+"#;
+        let summary = parser.parse(&PathBuf::from("test.dart"), content);
+        assert_eq!(summary.language, Language::Dart);
+        assert_eq!(summary.symbols.len(), 3);
+    }
+}

+ 126 - 0
src/parser/generic.rs

@@ -0,0 +1,126 @@
+//! Generic regex-based parser for unsupported languages.
+
+use regex::Regex;
+use std::path::PathBuf;
+
+use crate::models::{FileSummary, Language, Symbol, SymbolKind};
+use super::extract_doc;
+
+/// Generic parser using regex patterns for unknown languages.
+pub struct GenericParser;
+
+impl GenericParser {
+    pub fn new() -> Self {
+        Self
+    }
+
+    fn parse_content(&self, content: &str) -> Vec<Symbol> {
+        let mut symbols = Vec::new();
+        let lines: Vec<&str> = content.lines().collect();
+
+        // Generic function pattern: word followed by ( ... )
+        let func_regex = Regex::new(r"^(\w+)\s*\([^)]*\)\s*(?:\{|;)").unwrap();
+        
+        // Generic class/struct pattern
+        let class_regex = Regex::new(r"^(class|struct|interface|type|enum)\s+(\w+)").unwrap();
+        
+        // Generic const/var pattern
+        let const_regex = Regex::new(r"^(const|let|var|static|final)\s+(\w+)").unwrap();
+        
+        // Generic function definition pattern
+        let def_regex = Regex::new(r"^(def|fn|func|function|sub|procedure)\s+(\w+)").unwrap();
+
+        for (line_num, line) in lines.iter().enumerate() {
+            let trimmed = line.trim();
+            let line_number = line_num + 1;
+
+            // Skip comments and empty lines
+            if trimmed.is_empty() || trimmed.starts_with("//") 
+               || trimmed.starts_with('#') || trimmed.starts_with("/*") {
+                continue;
+            }
+
+            // Check for class/struct/interface/type/enum
+            if let Some(caps) = class_regex.captures(trimmed) {
+                let kind_str = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let name = caps.get(2).map(|m| m.as_str()).unwrap_or("");
+                
+                let kind = match kind_str {
+                    "class" => SymbolKind::Class,
+                    "struct" => SymbolKind::Struct,
+                    "interface" => SymbolKind::Interface,
+                    "enum" => SymbolKind::Enum,
+                    "type" => SymbolKind::TypeAlias,
+                    _ => SymbolKind::Struct,
+                };
+                
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for const/let/var/static/final
+            else if let Some(caps) = const_regex.captures(trimmed) {
+                let name = caps.get(2).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Const,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for function definition keywords
+            else if let Some(caps) = def_regex.captures(trimmed) {
+                let name = caps.get(2).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for generic function-like patterns
+            else if let Some(caps) = func_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                // Skip common keywords and short names
+                if name.len() < 3 || ["if", "else", "for", "while", "switch", "case", "return"].contains(&name) {
+                    continue;
+                }
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+        }
+
+        symbols
+    }
+}
+
+impl super::LanguageParser for GenericParser {
+    fn parse(&self, path: &PathBuf, content: &str) -> FileSummary {
+        let ext = path.extension()
+            .map(|e| e.to_string_lossy().to_string())
+            .unwrap_or_default();
+        
+        let language = Language::from_extension(&ext);
+
+        FileSummary {
+            path: path.clone(),
+            language,
+            symbols: self.parse_content(content),
+        }
+    }
+}

+ 172 - 0
src/parser/go.rs

@@ -0,0 +1,172 @@
+//! Go language parser.
+
+use regex::Regex;
+use std::path::PathBuf;
+
+use crate::models::{FileSummary, Language, Symbol, SymbolKind};
+use super::extract_doc;
+
+/// Parser for Go source files.
+pub struct GoParser;
+
+impl GoParser {
+    pub fn new() -> Self {
+        Self
+    }
+
+    fn parse_content(&self, content: &str) -> Vec<Symbol> {
+        let mut symbols = Vec::new();
+        let lines: Vec<&str> = content.lines().collect();
+        let total_lines = lines.len();
+
+        // Function/Method pattern: func name(...) or func (receiver) name(...)
+        let func_regex = Regex::new(r"^func\s+(?:\(([^)]+)\)\s+)?(\w+)\s*\(").unwrap();
+        
+        // Type pattern: type Name struct/interface/etc.
+        let type_regex = Regex::new(r"^type\s+(\w+)\s+(struct|interface|enum|union)").unwrap();
+        
+        // Const pattern: const Name or const (Name
+        let const_regex = Regex::new(r"^const\s+(?:\(([^)]+)\)\s*$|\w+)").unwrap();
+        
+        // Var pattern: var Name or var (Name
+        let var_regex = Regex::new(r"^var\s+(?:\(([^)]+)\)\s*$|\w+)").unwrap();
+
+        for (line_num, line) in lines.iter().enumerate() {
+            let trimmed = line.trim();
+            let line_number = line_num + 1;
+
+            // Skip comments and empty lines
+            if trimmed.is_empty() || trimmed.starts_with("//") || trimmed.starts_with("/*") {
+                continue;
+            }
+
+            // Check for function/method
+            if let Some(caps) = func_regex.captures(trimmed) {
+                let receiver = caps.get(1).map(|m| m.as_str());
+                let name = caps.get(2).map(|m| m.as_str()).unwrap_or("");
+                
+                // Extract full signature (rest of the line)
+                let signature = if let Some(rest_start) = trimmed.find('(') {
+                    trimmed[rest_start..].to_string()
+                } else {
+                    "()".to_string()
+                };
+
+                let kind = if receiver.is_some() {
+                    SymbolKind::Method
+                } else {
+                    SymbolKind::Function
+                };
+
+                let doc = extract_doc(&lines, line_number);
+                
+                let full_signature = if let Some(rec) = receiver {
+                    format!("({}) {}", rec, name) + &signature
+                } else {
+                    format!("{} {}", name, signature)
+                };
+
+                symbols.push(Symbol {
+                    kind,
+                    name: name.to_string(),
+                    signature: full_signature,
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for type definitions
+            else if let Some(caps) = type_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let type_kind = caps.get(2).map(|m| m.as_str()).unwrap_or("");
+
+                let kind = match type_kind {
+                    "struct" => SymbolKind::Struct,
+                    "interface" => SymbolKind::Interface,
+                    "enum" => SymbolKind::Enum,
+                    _ => SymbolKind::Struct,
+                };
+
+                let doc = extract_doc(&lines, line_number);
+
+                symbols.push(Symbol {
+                    kind,
+                    name: name.to_string(),
+                    signature: format!("type {} {}", name, type_kind),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for const declarations
+            else if trimmed.starts_with("const") && !trimmed.contains("const (") {
+                if let Some(caps) = const_regex.captures(trimmed) {
+                    if let Some(name) = caps.get(1) {
+                        let doc = extract_doc(&lines, line_number);
+                        symbols.push(Symbol {
+                            kind: SymbolKind::Const,
+                            name: name.as_str().to_string(),
+                            signature: trimmed.to_string(),
+                            doc,
+                            line: line_number,
+                        });
+                    }
+                }
+            }
+            // Check for var declarations
+            else if trimmed.starts_with("var") && !trimmed.contains("var (") {
+                if let Some(caps) = var_regex.captures(trimmed) {
+                    if let Some(name) = caps.get(1) {
+                        let doc = extract_doc(&lines, line_number);
+                        symbols.push(Symbol {
+                            kind: SymbolKind::Field,
+                            name: name.as_str().to_string(),
+                            signature: trimmed.to_string(),
+                            doc,
+                            line: line_number,
+                        });
+                    }
+                }
+            }
+        }
+
+        symbols
+    }
+}
+
+impl super::LanguageParser for GoParser {
+    fn parse(&self, path: &PathBuf, content: &str) -> FileSummary {
+        FileSummary {
+            path: path.clone(),
+            language: Language::Go,
+            symbols: self.parse_content(content),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::parser::LanguageParser;
+
+    #[test]
+    fn test_parse_go_function() {
+        let parser = GoParser::new();
+        let content = r#"
+package main
+
+// Login validates user credentials.
+func Login(user, pass string) (*Token, error) {
+    return nil, nil
+}
+
+// GetUser retrieves a user by ID.
+func (s *Service) GetUser(id string) (*User, error) {
+    return nil, nil
+}
+"#;
+        let summary = parser.parse(&PathBuf::from("test.go"), content);
+        assert_eq!(summary.language, Language::Go);
+        assert_eq!(summary.symbols.len(), 2);
+        assert_eq!(summary.symbols[0].name, "Login");
+        assert_eq!(summary.symbols[1].name, "GetUser");
+    }
+}

+ 105 - 0
src/parser/mod.rs

@@ -0,0 +1,105 @@
+//! Language parsers for extracting symbols from code files.
+
+mod go;
+mod rust;
+mod python;
+mod dart;
+mod r_lang;
+mod typescript;
+mod generic;
+
+pub use go::GoParser;
+pub use rust::RustParser;
+pub use python::PythonParser;
+pub use dart::DartParser;
+pub use r_lang::RParser;
+pub use typescript::TypeScriptParser;
+pub use generic::GenericParser;
+
+use crate::models::{FileSummary, Language};
+use std::path::PathBuf;
+
+/// Trait for language-specific parsers.
+pub trait LanguageParser {
+    /// Parse a file and extract symbols.
+    fn parse(&self, path: &PathBuf, content: &str) -> FileSummary;
+}
+
+/// Get the appropriate parser for a language.
+pub fn get_parser(language: &Language) -> Box<dyn LanguageParser> {
+    match language {
+        Language::Go => Box::new(GoParser),
+        Language::Rust => Box::new(RustParser),
+        Language::Python => Box::new(PythonParser),
+        Language::Dart => Box::new(DartParser),
+        Language::R => Box::new(RParser),
+        Language::TypeScript | Language::JavaScript => Box::new(TypeScriptParser),
+        _ => Box::new(GenericParser),
+    }
+}
+
+/// Extract documentation comments preceding a symbol.
+pub fn extract_doc(lines: &[&str], symbol_line: usize) -> Option<String> {
+    if symbol_line == 0 || symbol_line > lines.len() {
+        return None;
+    }
+
+    let mut doc_lines = Vec::new();
+    let start = if symbol_line > 1 { symbol_line - 1 } else { 0 };
+
+    // Look backwards for doc comments
+    for i in (0..start).rev() {
+        let line = lines[i].trim();
+        
+        // Stop if we hit a non-comment, non-empty line
+        if line.is_empty() {
+            continue;
+        }
+        
+        // Check for common doc comment patterns
+        let is_doc = line.starts_with("//") 
+            || line.starts_with("/*") 
+            || line.starts_with("'''")
+            || line.starts_with("\"\"\"")
+            || line.starts_with("#'")
+            || line.starts_with("# ");
+        
+        if is_doc {
+            // Clean up the comment
+            let cleaned = line
+                .trim_start_matches("//")
+                .trim_start_matches("/*")
+                .trim_start_matches("'''")
+                .trim_start_matches("\"\"\"")
+                .trim_start_matches("#'")
+                .trim_start_matches("# ")
+                .trim()
+                .to_string();
+            
+            if !cleaned.is_empty() {
+                doc_lines.insert(0, cleaned);
+            }
+        } else if !doc_lines.is_empty() {
+            // Non-empty, non-comment line after doc comments
+            break;
+        }
+    }
+
+    if doc_lines.is_empty() {
+        None
+    } else {
+        Some(doc_lines.join("\n"))
+    }
+}
+
+/// Parse a file using the appropriate parser based on its extension.
+pub fn parse_file(path: &PathBuf, content: &str) -> FileSummary {
+    let ext = path
+        .extension()
+        .map(|e| e.to_string_lossy().to_string())
+        .unwrap_or_default();
+    
+    let language = Language::from_extension(&ext);
+    let parser = get_parser(&language);
+    parser.parse(path, content)
+}

+ 135 - 0
src/parser/python.rs

@@ -0,0 +1,135 @@
+//! Python language parser.
+
+use regex::Regex;
+use std::path::PathBuf;
+
+use crate::models::{FileSummary, Language, Symbol, SymbolKind};
+use super::extract_doc;
+
+/// Parser for Python source files.
+pub struct PythonParser;
+
+impl PythonParser {
+    pub fn new() -> Self {
+        Self
+    }
+
+    fn parse_content(&self, content: &str) -> Vec<Symbol> {
+        let mut symbols = Vec::new();
+        let lines: Vec<&str> = content.lines().collect();
+
+        // Function pattern: def function_name(...)
+        let func_regex = Regex::new(r"^def\s+(\w+)\s*\(").unwrap();
+        
+        // Async function pattern: async def function_name(...)
+        let async_func_regex = Regex::new(r"^async\s+def\s+(\w+)\s*\(").unwrap();
+        
+        // Class pattern: class ClassName(...)
+        let class_regex = Regex::new(r"^class\s+(\w+)\s*(?:\([^)]*\))?").unwrap();
+        
+        // Decorator pattern: @decorator
+        let decorator_regex = Regex::new(r"^@\s*(\w+)").unwrap();
+
+        let mut current_decorator: Option<String> = None;
+
+        for (line_num, line) in lines.iter().enumerate() {
+            let trimmed = line.trim();
+            let line_number = line_num + 1;
+
+            // Skip comments and empty lines
+            if trimmed.is_empty() || trimmed.starts_with('#') {
+                continue;
+            }
+
+            // Check for decorator
+            if let Some(caps) = decorator_regex.captures(trimmed) {
+                let decorator_name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                current_decorator = Some(decorator_name.to_string());
+                continue;
+            }
+
+            // Check for async function
+            if let Some(caps) = async_func_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                let signature = trimmed.to_string();
+                
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature,
+                    doc,
+                    line: line_number,
+                });
+                current_decorator = None;
+            }
+            // Check for function
+            else if let Some(caps) = func_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                let signature = trimmed.to_string();
+                
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature,
+                    doc,
+                    line: line_number,
+                });
+                current_decorator = None;
+            }
+            // Check for class
+            else if let Some(caps) = class_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                let signature = trimmed.to_string();
+                
+                symbols.push(Symbol {
+                    kind: SymbolKind::Class,
+                    name: name.to_string(),
+                    signature,
+                    doc,
+                    line: line_number,
+                });
+                current_decorator = None;
+            }
+        }
+
+        symbols
+    }
+}
+
+impl super::LanguageParser for PythonParser {
+    fn parse(&self, path: &PathBuf, content: &str) -> FileSummary {
+        FileSummary {
+            path: path.clone(),
+            language: Language::Python,
+            symbols: self.parse_content(content),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::parser::LanguageParser;
+
+    #[test]
+    fn test_parse_python() {
+        let parser = PythonParser::new();
+        let content = r#"
+"""Module docstring."""
+
+def login(user, pass):
+    """Login function docstring."""
+    pass
+
+class User:
+    """User class docstring."""
+    pass
+"#;
+        let summary = parser.parse(&PathBuf::from("test.py"), content);
+        assert_eq!(summary.language, Language::Python);
+        assert_eq!(summary.symbols.len(), 2);
+    }
+}

+ 153 - 0
src/parser/r_lang.rs

@@ -0,0 +1,153 @@
+//! R language parser.
+
+use regex::Regex;
+use std::path::PathBuf;
+
+use crate::models::{FileSummary, Language, Symbol, SymbolKind};
+use super::extract_doc;
+
+/// Parser for R source files.
+pub struct RParser;
+
+impl RParser {
+    pub fn new() -> Self {
+        Self
+    }
+
+    fn parse_content(&self, content: &str) -> Vec<Symbol> {
+        let mut symbols = Vec::new();
+        let lines: Vec<&str> = content.lines().collect();
+
+        // Function pattern: function_name <- function(...) or function_name <- function(...)
+        let func_regex = Regex::new(r"^(\w+)\s*<-\s*function\s*\(").unwrap();
+        
+        // Function definition: function_name <- function(...) { ... }
+        let func_def_regex = Regex::new(r"^(\w+)\s*<-\s*function\s*\([^)]*\)").unwrap();
+        
+        // Class definition pattern: setClass("ClassName", ...)
+        let class_regex = Regex::new(r#"setClass\s*\(\s*["'](\w+)["']"#).unwrap();
+        
+        // S3 method: setMethod("method", ...)
+        let method_regex = Regex::new(r#"setMethod\s*\(\s*["'](\w+)["']"#).unwrap();
+        
+        // S4 method: setGeneric("generic", ...)
+        let generic_regex = Regex::new(r#"setGeneric\s*\(\s*["'](\w+)["']"#).unwrap();
+        
+        // Package exports: export(...) pattern in NAMESPACE files
+        let export_regex = Regex::new(r"^export\s*\(\s*([\w]+)").unwrap();
+
+        for (line_num, line) in lines.iter().enumerate() {
+            let trimmed = line.trim();
+            let line_number = line_num + 1;
+
+            // Skip comments and empty lines
+            if trimmed.is_empty() || trimmed.starts_with('#') {
+                continue;
+            }
+
+            // Check for function definition
+            if let Some(caps) = func_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                // Skip internal R functions
+                if name.starts_with('.') {
+                    continue;
+                }
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for class definition
+            else if let Some(caps) = class_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Class,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for method definition
+            else if let Some(caps) = method_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Method,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for generic definition
+            else if let Some(caps) = generic_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for export
+            else if let Some(caps) = export_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+        }
+
+        symbols
+    }
+}
+
+impl super::LanguageParser for RParser {
+    fn parse(&self, path: &PathBuf, content: &str) -> FileSummary {
+        FileSummary {
+            path: path.clone(),
+            language: Language::R,
+            symbols: self.parse_content(content),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::parser::LanguageParser;
+
+    #[test]
+    fn test_parse_r() {
+        let parser = RParser::new();
+        let content = r#"
+#' A simple function
+#' @param x The input value
+my_function <- function(x) {
+  # implementation
+}
+
+#' A class definition
+setClass("MyClass", representation(x = "numeric"))
+
+# Internal function (should be skipped)
+.internal_helper <- function() {}
+"#;
+        let summary = parser.parse(&PathBuf::from("test.R"), content);
+        assert_eq!(summary.language, Language::R);
+        assert_eq!(summary.symbols.len(), 2);
+    }
+}

+ 195 - 0
src/parser/rust.rs

@@ -0,0 +1,195 @@
+//! Rust language parser using regex-based extraction.
+
+use regex::Regex;
+use std::path::PathBuf;
+
+use crate::models::{FileSummary, Language, Symbol, SymbolKind};
+use super::extract_doc;
+
+/// Parser for Rust source files.
+pub struct RustParser;
+
+impl RustParser {
+    pub fn new() -> Self {
+        Self
+    }
+
+    fn parse_content(&self, content: &str) -> Vec<Symbol> {
+        let mut symbols = Vec::new();
+        let lines: Vec<&str> = content.lines().collect();
+
+        // Function pattern: pub fn name(...)
+        let func_regex = Regex::new(r"^pub\s+fn\s+(\w+)\s*\(").unwrap();
+        
+        // Struct pattern: pub struct Name
+        let struct_regex = Regex::new(r"^pub\s+struct\s+(\w+)").unwrap();
+        
+        // Enum pattern: pub enum Name
+        let enum_regex = Regex::new(r"^pub\s+enum\s+(\w+)").unwrap();
+        
+        // Trait pattern: pub trait Name
+        let trait_regex = Regex::new(r"^pub\s+trait\s+(\w+)").unwrap();
+        
+        // Impl pattern: impl Name or impl Trait for Name
+        let impl_regex = Regex::new(r"^impl\s+(?:(\w+)\s+for\s+)?(\w+)").unwrap();
+        
+        // Const pattern: pub const Name
+        let const_regex = Regex::new(r"^pub\s+const\s+(\w+)").unwrap();
+        
+        // Static pattern: pub static Name
+        let static_regex = Regex::new(r"^pub\s+static\s+(\w+)").unwrap();
+        
+        // Type pattern: pub type Name
+        let type_regex = Regex::new(r"^pub\s+type\s+(\w+)").unwrap();
+        
+        // Module pattern: pub mod Name
+        let mod_regex = Regex::new(r"^pub\s+mod\s+(\w+)").unwrap();
+        
+        for (line_num, line) in lines.iter().enumerate() {
+            let trimmed = line.trim();
+            let line_number = line_num + 1;
+
+            // Skip comments, empty lines, and use statements
+            if trimmed.is_empty() || trimmed.starts_with("//") || trimmed.starts_with("/*") || trimmed.starts_with("use ") {
+                continue;
+            }
+
+            if let Some(caps) = func_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            } else if let Some(caps) = struct_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Struct,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            } else if let Some(caps) = enum_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Enum,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            } else if let Some(caps) = trait_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Trait,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            } else if let Some(caps) = impl_regex.captures(trimmed) {
+                let trait_name = caps.get(1).map(|m| m.as_str());
+                let type_name = caps.get(2).map(|m| m.as_str()).unwrap_or("");
+                let name = trait_name.map(|t| format!("{} for {}", t, type_name))
+                    .unwrap_or_else(|| type_name.to_string());
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Trait,
+                    name,
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            } else if let Some(caps) = const_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Const,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            } else if let Some(caps) = static_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Static,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            } else if let Some(caps) = type_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::TypeAlias,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            } else if let Some(caps) = mod_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Module,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+        }
+
+        symbols
+    }
+}
+
+impl super::LanguageParser for RustParser {
+    fn parse(&self, path: &PathBuf, content: &str) -> FileSummary {
+        FileSummary {
+            path: path.clone(),
+            language: Language::Rust,
+            symbols: self.parse_content(content),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::parser::LanguageParser;
+
+    #[test]
+    fn test_parse_rust() {
+        let parser = RustParser::new();
+        let content = r#"
+/// A simple struct
+pub struct User {
+    name: String,
+}
+
+/// Login function
+pub fn login(user: &str, pass: &str) -> Result<(), Error> {
+    Ok(())
+}
+
+/// User trait
+pub trait UserTrait {
+    fn get_name(&self) -> String;
+}
+"#;
+        let summary = parser.parse(&PathBuf::from("test.rs"), content);
+        assert_eq!(summary.language, Language::Rust);
+        assert_eq!(summary.symbols.len(), 3);
+    }
+}

+ 249 - 0
src/parser/typescript.rs

@@ -0,0 +1,249 @@
+//! TypeScript/JavaScript language parser.
+
+use regex::Regex;
+use std::path::PathBuf;
+
+use crate::models::{FileSummary, Language, Symbol, SymbolKind};
+use super::extract_doc;
+
+/// Parser for TypeScript/JavaScript source files.
+pub struct TypeScriptParser;
+
+impl TypeScriptParser {
+    pub fn new() -> Self {
+        Self
+    }
+
+    fn parse_content(&self, content: &str) -> Vec<Symbol> {
+        let mut symbols = Vec::new();
+        let lines: Vec<&str> = content.lines().collect();
+
+        // Function pattern: function name(...) or async function name(...)
+        let func_regex = Regex::new(r"^(?:async\s+)?function\s+(\w+)\s*\(").unwrap();
+        
+        // Arrow function pattern: const name = (...) => or let name = (...) =>
+        let arrow_func_regex = Regex::new(r"^(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>").unwrap();
+        
+        // Method pattern: name(...) { or async name(...) {
+        let method_regex = Regex::new(r"^\s*(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?:\{|=>)").unwrap();
+        
+        // Class pattern: class ClassName
+        let class_regex = Regex::new(r"^class\s+(\w+)").unwrap();
+        
+        // Interface pattern: interface InterfaceName
+        let interface_regex = Regex::new(r"^interface\s+(\w+)").unwrap();
+        
+        // Type pattern: type TypeName =
+        let type_regex = Regex::new(r"^type\s+(\w+)").unwrap();
+        
+        // Enum pattern: enum EnumName
+        let enum_regex = Regex::new(r"^enum\s+(\w+)").unwrap();
+        
+        // Export pattern: export function/class/const/etc.
+        let export_func_regex = Regex::new(r"^export\s+(?:async\s+)?function\s+(\w+)").unwrap();
+        let export_const_regex = Regex::new(r"^export\s+(?:const|let|var)\s+(\w+)").unwrap();
+        let export_class_regex = Regex::new(r"^export\s+class\s+(\w+)").unwrap();
+        
+        // Import pattern: import ... from '...'
+        let import_regex = Regex::new(r#"^import\s+(?:\{[^}]+\}|\w+|\*\s+as\s+\w+)\s+from\s+["']([^"']+)["']"#).unwrap();
+
+        for (line_num, line) in lines.iter().enumerate() {
+            let trimmed = line.trim();
+            let line_number = line_num + 1;
+
+            // Skip comments and empty lines
+            if trimmed.is_empty() || trimmed.starts_with("//") || trimmed.starts_with("/*") {
+                continue;
+            }
+
+            // Check for export function
+            if let Some(caps) = export_func_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for export const/let/var
+            else if let Some(caps) = export_const_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Const,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for export class
+            else if let Some(caps) = export_class_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Class,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for function
+            else if let Some(caps) = func_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                // Skip constructor
+                if name == "constructor" {
+                    continue;
+                }
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for arrow function assignment
+            else if let Some(caps) = arrow_func_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Function,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for class
+            else if let Some(caps) = class_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Class,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for interface
+            else if let Some(caps) = interface_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Interface,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for type alias
+            else if let Some(caps) = type_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::TypeAlias,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for enum
+            else if let Some(caps) = enum_regex.captures(trimmed) {
+                let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Enum,
+                    name: name.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+            // Check for import
+            else if let Some(caps) = import_regex.captures(trimmed) {
+                let module = caps.get(1).map(|m| m.as_str()).unwrap_or("");
+                let doc = extract_doc(&lines, line_number);
+                symbols.push(Symbol {
+                    kind: SymbolKind::Import,
+                    name: module.to_string(),
+                    signature: trimmed.to_string(),
+                    doc,
+                    line: line_number,
+                });
+            }
+        }
+
+        symbols
+    }
+}
+
+impl super::LanguageParser for TypeScriptParser {
+    fn parse(&self, path: &PathBuf, content: &str) -> FileSummary {
+        let ext = path.extension()
+            .map(|e| e.to_string_lossy().to_string())
+            .unwrap_or_default();
+        
+        let language = if ext == "ts" || ext == "tsx" {
+            Language::TypeScript
+        } else {
+            Language::JavaScript
+        };
+
+        FileSummary {
+            path: path.clone(),
+            language,
+            symbols: self.parse_content(content),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::parser::LanguageParser;
+
+    #[test]
+    fn test_parse_typescript() {
+        let parser = TypeScriptParser::new();
+        let content = r#"
+/**
+ * User service class
+ */
+export class UserService {
+    /**
+     * Login method
+     */
+    login(user: string, pass: string): Promise<Token> {
+        // implementation
+    }
+}
+
+/**
+ * Config interface
+ */
+interface Config {
+    apiUrl: string;
+}
+
+/**
+ * Type alias for user
+ */
+type User = {
+    id: string;
+    name: string;
+};
+"#;
+        let summary = parser.parse(&PathBuf::from("test.ts"), content);
+        assert_eq!(summary.language, Language::TypeScript);
+        assert_eq!(summary.symbols.len(), 3);
+    }
+}

+ 148 - 0
src/scanner.rs

@@ -0,0 +1,148 @@
+//! Directory scanner for finding code files.
+
+use std::path::PathBuf;
+use walkdir::WalkDir;
+use anyhow::{Context, Result};
+
+use crate::models::Language;
+
+/// Scanner configuration.
+#[derive(Debug, Clone)]
+pub struct ScannerConfig {
+    /// Directories to exclude from scanning.
+    pub exclude_dirs: Vec<String>,
+    /// File extensions to include.
+    pub include_extensions: Vec<String>,
+    /// Whether to follow symlinks.
+    pub follow_symlinks: bool,
+}
+
+impl Default for ScannerConfig {
+    fn default() -> Self {
+        Self {
+            exclude_dirs: vec![
+                "target".to_string(),
+                "node_modules".to_string(),
+                ".git".to_string(),
+                ".svn".to_string(),
+                "vendor".to_string(),
+                "__pycache__".to_string(),
+                ".venv".to_string(),
+                "venv".to_string(),
+                "dist".to_string(),
+                "build".to_string(),
+                ".idea".to_string(),
+                ".vscode".to_string(),
+            ],
+            include_extensions: vec![
+                "go".to_string(),
+                "rs".to_string(),
+                "py".to_string(),
+                "dart".to_string(),
+                "r".to_string(),
+                "R".to_string(),
+                "ts".to_string(),
+                "tsx".to_string(),
+                "js".to_string(),
+                "jsx".to_string(),
+                "java".to_string(),
+                "c".to_string(),
+                "h".to_string(),
+                "cpp".to_string(),
+                "cc".to_string(),
+                "cxx".to_string(),
+                "hpp".to_string(),
+                "cs".to_string(),
+                "rb".to_string(),
+                "php".to_string(),
+                "swift".to_string(),
+                "kt".to_string(),
+            ],
+            follow_symlinks: false,
+        }
+    }
+}
+
+/// Scanner for finding code files in a directory tree.
+pub struct Scanner {
+    config: ScannerConfig,
+}
+
+impl Scanner {
+    /// Create a new scanner with the given configuration.
+    pub fn new(config: ScannerConfig) -> Self {
+        Self { config }
+    }
+
+    /// Create a scanner with default configuration.
+    pub fn default_config() -> Self {
+        Self::new(ScannerConfig::default())
+    }
+
+    /// Scan a directory and return all code file paths.
+    pub fn scan(&self, root: &PathBuf) -> Result<Vec<PathBuf>> {
+        let mut files = Vec::new();
+
+        let walker = WalkDir::new(root)
+            .follow_links(self.config.follow_symlinks)
+            .into_iter()
+            .filter_entry(|entry| {
+                // Skip excluded directories
+                if entry.file_type().is_dir() {
+                    if let Some(name) = entry.file_name().to_str() {
+                        return !self.config.exclude_dirs.contains(&name.to_string());
+                    }
+                }
+                true
+            });
+
+        for entry in walker.filter_map(|e| e.ok()) {
+            if entry.file_type().is_file() {
+                if let Some(ext) = entry.path().extension() {
+                    if self.config.include_extensions.contains(&ext.to_string_lossy().to_string()) {
+                        files.push(entry.path().to_path_buf());
+                    }
+                }
+            }
+        }
+
+        Ok(files)
+    }
+
+    /// Get the language for a file based on its extension.
+    pub fn get_language(path: &PathBuf) -> Language {
+        let ext = path
+            .extension()
+            .map(|e| e.to_string_lossy().to_string())
+            .unwrap_or_default();
+        Language::from_extension(&ext)
+    }
+}
+
+/// Read the contents of a file.
+pub fn read_file(path: &PathBuf) -> Result<String> {
+    std::fs::read_to_string(path)
+        .with_context(|| format!("Failed to read file: {}", path.display()))
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::env;
+
+    #[test]
+    fn test_scanner_default() {
+        let scanner = Scanner::default_config();
+        assert!(!scanner.config.exclude_dirs.is_empty());
+        assert!(!scanner.config.include_extensions.is_empty());
+    }
+
+    #[test]
+    fn test_get_language() {
+        assert_eq!(Scanner::get_language(&PathBuf::from("test.go")), Language::Go);
+        assert_eq!(Scanner::get_language(&PathBuf::from("test.rs")), Language::Rust);
+        assert_eq!(Scanner::get_language(&PathBuf::from("test.py")), Language::Python);
+        assert_eq!(Scanner::get_language(&PathBuf::from("test.dart")), Language::Dart);
+        assert_eq!(Scanner::get_language(&PathBuf::from("test.ts")), Language::TypeScript);
+    }
+}

+ 111 - 0
src/writer.rs

@@ -0,0 +1,111 @@
+//! Markdown output writer.
+
+use std::path::PathBuf;
+use crate::models::{CodebaseSummary, FileSummary, Symbol, SymbolKind};
+
+pub const AGENTS_INSTRUCTIONS: &str = r#"# Codebase Navigation Protocol
+
+You have access to codebase_summary.md. This file contains a compressed map of the codebase, including exported signatures and their associated documentation comments.
+
+Protocol:
+
+Consult Summary First: Use the doc-strings in codebase_summary.md to understand the intent and usage of functions without reading the full implementation.
+Identify Targets: Identify the specific files containing the logic you need.
+Deep Dive: Open the identified files to study the internal logic/edge cases before proposing a solution.
+After making changes: run the codebase_summarizer tool to update the codebase_summary.md again if you changed the directory structure, added or removed code files, changed function signatures, changed comments
+or if you see otherwise necessary
+"#;
+
+/// Writer for generating markdown output.
+pub struct MarkdownWriter;
+
+impl MarkdownWriter {
+    pub fn new() -> Self {
+        Self
+    }
+
+    /// Write the codebase summary to a markdown file.
+    pub fn write_summary(&self, summary: &CodebaseSummary, output_path: &PathBuf) -> std::io::Result<()> {
+        let mut content = String::new();
+        
+        content.push_str("# Codebase Architecture Map\n\n");
+        
+        // Sort files by path for consistent output
+        let mut files = summary.files.clone();
+        files.sort_by(|a, b| a.path.cmp(&b.path));
+        
+        for file_summary in files {
+            self.write_file_summary(&mut content, &file_summary);
+        }
+        
+        std::fs::write(output_path, content)?;
+        Ok(())
+    }
+
+    fn write_file_summary(&self, content: &mut String, file_summary: &FileSummary) {
+        // Only write files that have symbols
+        if file_summary.symbols.is_empty() {
+            return;
+        }
+        
+        let relative_path = self.get_relative_path(&file_summary.path);
+        content.push_str(&format!("## File: {}\n", relative_path));
+        content.push('\n');
+        
+        for symbol in &file_summary.symbols {
+            self.write_symbol(content, symbol);
+        }
+        
+        content.push('\n');
+    }
+
+    fn write_symbol(&self, content: &mut String, symbol: &Symbol) {
+        let kind_str = match symbol.kind {
+            SymbolKind::Function => "func",
+            SymbolKind::Method => "method",
+            SymbolKind::Struct => "struct",
+            SymbolKind::Enum => "enum",
+            SymbolKind::Class => "class",
+            SymbolKind::Interface => "interface",
+            SymbolKind::Trait => "trait",
+            SymbolKind::TypeAlias => "type",
+            SymbolKind::Const => "const",
+            SymbolKind::Static => "static",
+            SymbolKind::Field => "field",
+            SymbolKind::Module => "module",
+            SymbolKind::Import => "import",
+        };
+        
+        content.push_str(&format!("### {} {}\n", kind_str, symbol.name));
+        
+        if let Some(doc) = &symbol.doc {
+            content.push_str(&format!("**Doc:** \n{}\n", doc));
+        } else {
+            content.push_str("**Doc:** \n");
+        }
+        
+        content.push('\n');
+    }
+
+    fn get_relative_path(&self, path: &PathBuf) -> String {
+        path.to_string_lossy().to_string()
+    }
+
+    /// Write the AGENTS.md file if it doesn't exist.
+    pub fn write_agents_md(&self, output_dir: &PathBuf) -> std::io::Result<bool> {
+        let agents_path = output_dir.join("AGENTS.md");
+        
+        if agents_path.exists() {
+            return Ok(false);
+        }
+        
+        std::fs::write(&agents_path, AGENTS_INSTRUCTIONS)?;
+        Ok(true)
+    }
+}
+
+impl Default for MarkdownWriter {
+    fn default() -> Self {
+        Self::new()
+    }
+}

+ 227 - 0
tests/integration_tests.rs

@@ -0,0 +1,227 @@
+use std::path::PathBuf;
+use tempfile::TempDir;
+
+#[test]
+fn test_scanner_finds_all_sample_files() {
+    let samples_dir = PathBuf::from("tests/samples");
+    let scanner = codebase_summarizer::scanner::Scanner::default_config();
+    let files = scanner.scan(&samples_dir).unwrap();
+
+    assert!(files.len() >= 12, "Should find at least 12 sample files");
+}
+
+#[test]
+fn test_parse_go_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.go");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::GoParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::Go);
+    assert!(summary.symbols.len() >= 4, "Should find functions, structs, const");
+}
+
+#[test]
+fn test_parse_rust_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.rs");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::RustParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::Rust);
+    assert!(summary.symbols.len() >= 5, "Should find struct, functions, const, type");
+}
+
+#[test]
+fn test_parse_python_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.py");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::PythonParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::Python);
+    assert!(summary.symbols.len() >= 3, "Should find functions and class");
+}
+
+#[test]
+fn test_parse_dart_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.dart");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::DartParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::Dart);
+    assert!(summary.symbols.len() >= 4, "Should find class, enum, methods");
+}
+
+#[test]
+fn test_parse_typescript_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.ts");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::TypeScriptParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::TypeScript);
+    assert!(summary.symbols.len() >= 5, "Should find class, interface, type, enum, const");
+}
+
+#[test]
+fn test_parse_r_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.r");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::RParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::R);
+    assert!(summary.symbols.len() >= 2, "Should find function and class");
+}
+
+#[test]
+fn test_parse_java_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.java");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::GenericParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::Java);
+    assert!(summary.symbols.len() >= 3, "Should find class, interface, enum");
+}
+
+#[test]
+fn test_parse_c_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.c");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::GenericParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::C);
+    assert!(summary.symbols.len() >= 3, "Should find struct, functions, const");
+}
+
+#[test]
+fn test_parse_ruby_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.rb");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::GenericParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::Ruby);
+    assert!(summary.symbols.len() >= 2, "Should find class and module");
+}
+
+#[test]
+fn test_parse_php_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.php");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::GenericParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::Php);
+    assert!(summary.symbols.len() >= 2, "Should find class and interface");
+}
+
+#[test]
+fn test_parse_swift_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.swift");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::GenericParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::Swift);
+    assert!(summary.symbols.len() >= 2, "Should find struct and func");
+}
+
+#[test]
+fn test_parse_kotlin_sample() {
+    use codebase_summarizer::parser::LanguageParser;
+    let path = PathBuf::from("tests/samples/sample.kt");
+    let content = std::fs::read_to_string(&path).unwrap();
+    let parser = codebase_summarizer::parser::GenericParser;
+    let summary = parser.parse(&path, &content);
+
+    assert_eq!(summary.language, codebase_summarizer::models::Language::Kotlin);
+    assert!(summary.symbols.len() >= 3, "Should find data class, functions, enum");
+}
+
+#[test]
+fn test_parser_factory() {
+    use codebase_summarizer::parser::{get_parser, LanguageParser};
+
+    let go_parser = get_parser(&codebase_summarizer::models::Language::Go);
+    let rust_parser = get_parser(&codebase_summarizer::models::Language::Rust);
+    let python_parser = get_parser(&codebase_summarizer::models::Language::Python);
+    let generic_parser = get_parser(&codebase_summarizer::models::Language::Cpp);
+
+    assert!(go_parser.parse(&PathBuf::from("test.go"), "").language == codebase_summarizer::models::Language::Go);
+    assert!(rust_parser.parse(&PathBuf::from("test.rs"), "").language == codebase_summarizer::models::Language::Rust);
+    assert!(python_parser.parse(&PathBuf::from("test.py"), "").language == codebase_summarizer::models::Language::Python);
+    assert!(generic_parser.parse(&PathBuf::from("test.cpp"), "").language == codebase_summarizer::models::Language::Cpp);
+}
+
+#[test]
+fn test_writer_generates_markdown() {
+    use codebase_summarizer::models::{CodebaseSummary, FileSummary, Symbol, SymbolKind};
+    use codebase_summarizer::writer::MarkdownWriter;
+
+    let temp_dir = TempDir::new().unwrap();
+    let output_path = temp_dir.path().join("summary.md");
+
+    let symbols = vec![
+        Symbol {
+            kind: SymbolKind::Function,
+            name: "test_func".to_string(),
+            signature: "fn test_func()".to_string(),
+            doc: Some("Test function".to_string()),
+            line: 1,
+        },
+    ];
+
+    let file_summary = FileSummary {
+        path: PathBuf::from("test.rs"),
+        language: codebase_summarizer::models::Language::Rust,
+        symbols,
+    };
+
+    let summary = CodebaseSummary::new(temp_dir.path().to_path_buf());
+    let mut summary = summary;
+    summary.add_file(file_summary);
+
+    let writer = MarkdownWriter::new();
+    writer.write_summary(&summary, &output_path).unwrap();
+
+    assert!(output_path.exists());
+    let content = std::fs::read_to_string(&output_path).unwrap();
+    assert!(content.contains("test_func"));
+}
+
+#[test]
+fn test_language_from_extension() {
+    use codebase_summarizer::models::Language;
+
+    assert_eq!(Language::from_extension("go"), Language::Go);
+    assert_eq!(Language::from_extension("rs"), Language::Rust);
+    assert_eq!(Language::from_extension("py"), Language::Python);
+    assert_eq!(Language::from_extension("dart"), Language::Dart);
+    assert_eq!(Language::from_extension("ts"), Language::TypeScript);
+    assert_eq!(Language::from_extension("r"), Language::R);
+    assert_eq!(Language::from_extension("R"), Language::R);
+    assert_eq!(Language::from_extension("java"), Language::Java);
+    assert_eq!(Language::from_extension("c"), Language::C);
+    assert_eq!(Language::from_extension("cpp"), Language::Cpp);
+    assert_eq!(Language::from_extension("rb"), Language::Ruby);
+    assert_eq!(Language::from_extension("php"), Language::Php);
+    assert_eq!(Language::from_extension("swift"), Language::Swift);
+    assert_eq!(Language::from_extension("kt"), Language::Kotlin);
+    assert_eq!(Language::from_extension("unknown"), Language::Unknown);
+}

+ 25 - 0
tests/samples/sample.c

@@ -0,0 +1,25 @@
+#include <stdio.h>
+
+struct User {
+    char* name;
+    char* email;
+};
+
+typedef struct {
+    int id;
+    char* name;
+} Admin;
+
+void login(const char* user, const char* pass) {
+    printf("Logging in %s\n", user);
+}
+
+int logout(char* user) {
+    return 0;
+}
+
+static void helper() {
+    printf("Helper\n");
+}
+
+const int MAX_RETRIES = 3;

+ 29 - 0
tests/samples/sample.dart

@@ -0,0 +1,29 @@
+/// A simple Dart class for user service.
+class UserService {
+  /// Login method validates credentials.
+  void login(String user, String pass) {
+    // implementation
+  }
+
+  /// Logout method clears session.
+  void logout() {
+    // implementation
+  }
+
+  /// Static helper method.
+  static void helper() {
+    // implementation
+  }
+}
+
+/// Enum representing user status.
+enum UserStatus {
+  active,
+  inactive,
+  suspended,
+}
+
+/// Abstract class for base service.
+abstract class BaseService {
+  void init();
+}

+ 30 - 0
tests/samples/sample.go

@@ -0,0 +1,30 @@
+package main
+
+// Login validates user credentials.
+func Login(user, pass string) (*Token, error) {
+    return nil, nil
+}
+
+// GetUser retrieves a user by ID.
+func (s *Service) GetUser(id string) (*User, error) {
+    return nil, nil
+}
+
+// User represents a user entity.
+type User struct {
+    ID   string
+    Name string
+}
+
+// Service handles user operations.
+type Service struct {
+    db *Database
+}
+
+// Token represents an authentication token.
+type Token struct {
+    Value string
+}
+
+// const MaxRetries defines maximum login attempts.
+const MaxRetries = 3

+ 20 - 0
tests/samples/sample.java

@@ -0,0 +1,20 @@
+class UserService {
+    String name;
+    String email;
+}
+
+interface Runnable {
+    void run();
+}
+
+enum UserStatus {
+    ACTIVE,
+    INACTIVE
+}
+
+void login() {
+}
+
+String getName() {
+    return "";
+}

+ 19 - 0
tests/samples/sample.kt

@@ -0,0 +1,19 @@
+class User {
+    String name;
+    String email;
+}
+
+interface Runnable {
+    void run();
+}
+
+enum UserStatus {
+    ACTIVE,
+    INACTIVE
+}
+
+fun login() {
+}
+
+fun helper() {
+}

+ 25 - 0
tests/samples/sample.php

@@ -0,0 +1,25 @@
+<?php
+
+class UserService {
+    private $name;
+    private $email;
+
+    public function __construct($name, $email) {
+        $this->name = $name;
+        $this->email = $email;
+    }
+
+    public function login($user, $pass) {
+        return true;
+    }
+
+    public static function helper() {
+        return "helper";
+    }
+}
+
+interface Runnable {
+    public function run();
+}
+
+const MAX_RETRIES = 3;

+ 24 - 0
tests/samples/sample.py

@@ -0,0 +1,24 @@
+"""Module docstring for sample module."""
+
+def login(user, pass):
+    """Login function validates credentials."""
+    pass
+
+def logout(user):
+    """Logout function clears session."""
+    pass
+
+class User:
+    """User class represents a user entity."""
+    def __init__(self, name, email):
+        self.name = name
+        self.email = email
+
+    def get_name(self):
+        """Get the user's name."""
+        return self.name
+
+@decorator
+def decorated_function():
+    """A function with a decorator."""
+    pass

+ 14 - 0
tests/samples/sample.r

@@ -0,0 +1,14 @@
+#' A simple function for data processing
+#' @param x The input value
+#' @param y The multiplier
+my_function <- function(x, y) {
+  # implementation
+}
+
+#' A class definition for S4
+setClass("MyClass", representation(x = "numeric", y = "character"))
+
+#' A method definition
+setMethod("print", "MyClass", function(x, ...) {
+  cat("MyClass object\n")
+})

+ 20 - 0
tests/samples/sample.rb

@@ -0,0 +1,20 @@
+class UserService
+  def initialize(name, email)
+    @name = name
+    @email = email
+  end
+
+  def login(user, pass)
+    # login implementation
+  end
+
+  def self.helper
+    # static helper
+  end
+end
+
+module HelperModule
+  def self.process
+    # module function
+  end
+end

+ 31 - 0
tests/samples/sample.rs

@@ -0,0 +1,31 @@
+/// A simple struct for user data
+pub struct User {
+    name: String,
+    email: String,
+}
+
+/// Login function validates credentials
+pub fn login(user: &str, pass: &str) -> Result<(), Error> {
+    Ok(())
+}
+
+/// User trait defines user-related behaviors
+pub trait UserTrait {
+    fn get_name(&self) -> String;
+}
+
+/// Impl block for User
+impl User {
+    pub fn new(name: String) -> Self {
+        Self { name, email: String::new() }
+    }
+}
+
+/// Constant for maximum retry attempts
+pub const MAX_RETRIES: u32 = 3;
+
+/// Static variable for tracking total logins
+pub static mut TOTAL_LOGINS: u32 = 0;
+
+/// Type alias for user ID
+pub type UserId = String;

+ 17 - 0
tests/samples/sample.swift

@@ -0,0 +1,17 @@
+struct User {
+    let name: String
+    let email: String
+}
+
+func login(user: String, pass: String) -> Bool {
+    return true
+}
+
+static func helper() {
+    return "helper"
+}
+
+enum UserStatus {
+    case active
+    case inactive
+}

+ 47 - 0
tests/samples/sample.ts

@@ -0,0 +1,47 @@
+/**
+ * User service class for authentication
+ */
+export class UserService {
+    /**
+     * Login method
+     */
+    login(user: string, pass: string): Promise<Token> {
+        // implementation
+    }
+}
+
+/**
+ * Config interface for app configuration
+ */
+interface Config {
+    apiUrl: string;
+    timeout: number;
+}
+
+/**
+ * Type alias for user
+ */
+type User = {
+    id: string;
+    name: string;
+};
+
+/**
+ * Enum for user status
+ */
+enum UserStatus {
+    Active = 1,
+    Inactive = 2,
+}
+
+/**
+ * Arrow function for creating user
+ */
+export const createUser = (name: string): User => {
+    return { id: '', name };
+};
+
+/**
+ * Constant for max retries
+ */
+export const MAX_RETRIES = 3;