use std::sync::OnceLock;
const README: &str = include_str!("../README.md");
const SECTION: &str = "### Setting it up in your client";
const HTTP_SECTION: &str = "### Connecting over HTTP";
const KEY_PLACEHOLDER: &str = "${SEMLITH_AGENT_KEY}";
#[derive(Debug, Clone, serde::Serialize)]
pub struct Stanza {
pub format: String,
pub text: String,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct Client {
pub name: String,
pub note: String,
pub group: String,
pub stanzas: Vec<Stanza>,
}
pub fn http_stanzas(key: &str) -> Vec<Stanza> {
static PARSED: OnceLock<Vec<Stanza>> = OnceLock::new();
PARSED
.get_or_init(|| parse_http(README))
.iter()
.map(|stanza| Stanza {
format: stanza.format.clone(),
text: stanza.text.replace(KEY_PLACEHOLDER, key),
})
.collect()
}
fn parse_http(readme: &str) -> Vec<Stanza> {
let Some(start) = readme.find(HTTP_SECTION) else {
return Vec::new();
};
let rest = &readme[start + HTTP_SECTION.len()..];
let section = match rest.find("\n## ") {
Some(end) => &rest[..end],
None => rest,
};
let mut out = Vec::new();
let mut lines = section.lines();
while let Some(line) = lines.next() {
let Some(format) = line.strip_prefix("```") else {
continue;
};
let mut text = String::new();
for body in lines.by_ref() {
if body.starts_with("```") {
break;
}
text.push_str(body);
text.push('\n');
}
out.push(Stanza {
format: format.trim().to_string(),
text,
});
}
out
}
pub fn clients() -> &'static [Client] {
static PARSED: OnceLock<Vec<Client>> = OnceLock::new();
PARSED.get_or_init(|| parse(README))
}
fn parse(readme: &str) -> Vec<Client> {
let Some(start) = readme.find(SECTION) else {
return Vec::new();
};
let rest = &readme[start + SECTION.len()..];
let end = ["\n## ", "\n### "]
.iter()
.filter_map(|marker| rest.find(marker))
.min();
let section = match end {
Some(end) => &rest[..end],
None => rest,
};
let mut out: Vec<Client> = Vec::new();
let mut group = String::from("terminal");
let mut lines = section.lines().peekable();
while let Some(line) = lines.next() {
if let Some(heading) = line.strip_prefix("#### ") {
group = match heading.trim() {
"Editors" => "editor",
"Desktop apps" => "desktop",
_ => "terminal",
}
.to_string();
continue;
}
let Some(after) = line.strip_prefix("**") else {
continue;
};
let Some((name, tail)) = after.split_once("**") else {
continue;
};
let mut note = tail.trim_start_matches([' ', '—', '-']).trim().to_string();
for next in lines.by_ref() {
if next.trim().is_empty() {
break;
}
if next.starts_with("```") || next.starts_with("**") {
break;
}
note.push(' ');
note.push_str(next.trim());
}
let mut stanzas = Vec::new();
while let Some(peeked) = lines.peek() {
if peeked.starts_with("**") || peeked.starts_with("#### ") {
break;
}
let line = lines.next().expect("just peeked");
let Some(format) = line.strip_prefix("```") else {
continue;
};
let mut text = String::new();
for body in lines.by_ref() {
if body.starts_with("```") {
break;
}
text.push_str(body);
text.push('\n');
}
stanzas.push(Stanza {
format: format.trim().to_string(),
text,
});
}
if !stanzas.is_empty() {
out.push(Client {
name: name.trim().to_string(),
note: squeeze(¬e),
group: group.clone(),
stanzas,
});
}
}
out
}
fn squeeze(note: &str) -> String {
note.replace("**", "")
.replace('`', "")
.split_whitespace()
.collect::<Vec<_>>()
.join(" ")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_documented_client_is_parsed_with_at_least_one_stanza() {
let parsed = clients();
assert!(
parsed.len() >= 27,
"only {} clients parsed out of the README",
parsed.len()
);
for client in parsed {
assert!(!client.name.is_empty());
assert!(!client.stanzas.is_empty(), "{} has no stanza", client.name);
for stanza in &client.stanzas {
assert!(
!stanza.text.trim().is_empty(),
"{} has an empty stanza",
client.name
);
}
}
}
#[test]
fn no_parsed_stanza_carries_a_store_path() {
for client in clients() {
for stanza in &client.stanzas {
assert!(
!stanza.text.contains("--store"),
"{} still passes --store:\n{}",
client.name,
stanza.text
);
}
}
}
#[test]
fn every_client_carries_a_group_read_from_the_readme() {
let parsed = clients();
let mut counts = std::collections::BTreeMap::new();
for client in parsed {
assert!(
["terminal", "editor", "desktop"].contains(&client.group.as_str()),
"{} is in group {:?}",
client.name,
client.group
);
*counts.entry(client.group.as_str()).or_insert(0usize) += 1;
}
assert_eq!(
counts.values().sum::<usize>(),
parsed.len(),
"the groups do not cover every client"
);
assert_eq!(counts.len(), 3, "a group is empty: {counts:?}");
}
#[test]
fn the_http_stanzas_carry_the_key_they_are_given() {
let stanzas = http_stanzas("sml_abc123");
assert!(!stanzas.is_empty(), "no HTTP stanza was parsed");
for stanza in &stanzas {
assert!(
stanza.text.contains("127.0.0.1:7365/mcp"),
"an HTTP stanza does not name the endpoint:\n{}",
stanza.text
);
assert!(
stanza.text.contains("Bearer sml_abc123"),
"an HTTP stanza does not carry the key it was given:\n{}",
stanza.text
);
assert!(
!stanza.text.contains(KEY_PLACEHOLDER),
"an HTTP stanza still carries the placeholder:\n{}",
stanza.text
);
}
}
#[test]
fn claude_code_is_the_first_client_and_has_both_of_its_forms() {
let first = &clients()[0];
assert_eq!(first.name, "Claude Code");
let formats: Vec<&str> = first.stanzas.iter().map(|s| s.format.as_str()).collect();
assert_eq!(formats, vec!["json", "sh", "sh"]);
assert!(
first.stanzas[0].text.contains("http://127.0.0.1:7365/mcp"),
"the first stanza is not the endpoint:\n{}",
first.stanzas[0].text
);
assert_eq!(
first.stanzas[2].text.trim(),
"claude mcp add semlith -- semlith mcp"
);
}
}