use anyhow::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct IncludeSet {
pub subagents: bool,
pub mcp: bool,
pub tool_output: bool,
pub history: bool,
}
impl IncludeSet {
pub fn status_quo() -> Self {
Self {
subagents: true,
mcp: false,
tool_output: false,
history: false,
}
}
pub fn all() -> Self {
Self {
subagents: true,
mcp: true,
tool_output: true,
history: true,
}
}
pub fn none() -> Self {
Self::default()
}
pub fn parse(s: &str) -> Result<Self> {
let mut set = Self::default();
let mut saw_all = false;
let mut saw_none = false;
let mut saw_any_meaningful = false;
for (idx, raw) in s.split(',').enumerate() {
let token = raw.trim().to_ascii_lowercase();
if token.is_empty() {
if s.trim().is_empty() && idx == 0 {
continue;
}
anyhow::bail!(
"empty token in --include list (got '{}'); \
remove the stray comma or use --include none",
s
);
}
match token.as_str() {
"all" => {
saw_all = true;
set = Self::all();
saw_any_meaningful = true;
}
"none" => {
saw_none = true;
set = Self::none();
saw_any_meaningful = true;
}
"subagents" => {
set.subagents = true;
saw_any_meaningful = true;
}
"mcp" => {
set.mcp = true;
saw_any_meaningful = true;
}
"tool-output" => {
set.tool_output = true;
saw_any_meaningful = true;
}
"history" => {
set.history = true;
saw_any_meaningful = true;
}
other => {
eprintln!(
"warning: ignoring unknown --include token '{}' \
(recognized: subagents, mcp, tool-output, history, all, none)",
other
);
}
}
}
if saw_all && saw_none {
anyhow::bail!(
"--include cannot contain both 'all' and 'none' (got '{}'); \
pick one — they are mutually exclusive",
s
);
}
let _ = saw_any_meaningful;
Ok(set)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn status_quo_matches_pre_pr2_default() {
let s = IncludeSet::status_quo();
assert!(s.subagents);
assert!(!s.mcp);
assert!(!s.tool_output);
assert!(!s.history);
}
#[test]
fn parse_single_token() {
let s = IncludeSet::parse("subagents").unwrap();
assert_eq!(s, IncludeSet::status_quo());
}
#[test]
fn parse_multiple_tokens() {
let s = IncludeSet::parse("subagents,mcp,history").unwrap();
assert!(s.subagents);
assert!(s.mcp);
assert!(!s.tool_output);
assert!(s.history);
}
#[test]
fn parse_canonical_tool_output_token() {
let a = IncludeSet::parse("tool-output").unwrap();
assert!(a.tool_output);
let b = IncludeSet::parse("tool_output").unwrap();
assert!(!b.tool_output, "tool_output is no longer recognized");
}
#[test]
fn parse_case_insensitive() {
let s = IncludeSet::parse("SubAgents,MCP,Tool-Output,HISTORY").unwrap();
assert_eq!(s, IncludeSet::all());
}
#[test]
fn parse_all_token() {
let s = IncludeSet::parse("all").unwrap();
assert_eq!(s, IncludeSet::all());
}
#[test]
fn parse_none_token() {
let s = IncludeSet::parse("none").unwrap();
assert_eq!(s, IncludeSet::none());
}
#[test]
fn parse_empty_string_is_none() {
let s = IncludeSet::parse("").unwrap();
assert_eq!(s, IncludeSet::none());
}
#[test]
fn parse_unknown_token_warns_and_skips() {
let s = IncludeSet::parse("subagents,bogus,mcp").unwrap();
assert!(s.subagents);
assert!(s.mcp);
assert!(!s.tool_output);
}
#[test]
fn parse_trims_whitespace() {
let s = IncludeSet::parse(" subagents , mcp ").unwrap();
assert!(s.subagents);
assert!(s.mcp);
}
#[test]
fn parse_all_and_none_together_is_rejected() {
let err = IncludeSet::parse("all,none").unwrap_err();
let msg = format!("{err:#}");
assert!(msg.contains("'all'") || msg.contains("all"));
assert!(msg.contains("'none'") || msg.contains("none"));
}
#[test]
fn parse_none_then_subagents_re_enables() {
let s = IncludeSet::parse("none,subagents").unwrap();
assert!(s.subagents);
assert!(!s.mcp);
}
#[test]
fn parse_empty_token_between_commas_is_rejected() {
let err = IncludeSet::parse("subagents,,mcp").unwrap_err();
let msg = format!("{err:#}");
assert!(msg.contains("empty token"), "got: {msg}");
}
}