Skip to main content

jev_repl/
presets.rs

1//! Ready-made sessions to poke at: `:preset <name>`.
2
3use crate::session::{self, Session};
4
5pub struct Preset {
6    pub name: &'static str,
7    pub about: &'static str,
8    /// Commands replayed as if typed.
9    pub script: &'static [&'static str],
10}
11
12pub const PRESETS: &[Preset] = &[
13    Preset {
14        name: "triage",
15        about: "Route a support ticket: department, frustration, urgency",
16        script: &[
17            ":state Hi, I've been trying to connect my Stripe account for 3 days and it keeps failing. I'm losing sales. Please help ASAP.",
18            ":choice department Which team should handle this | billing=Payment or subscription issues | technical=Bugs or integration problems | sales=Pricing or account questions",
19            ":score frustration How frustrated the customer appears | Calm, just stating facts | Frustrated but civil | Very angry, strong language",
20            ":noul is_urgent The message conveys urgency or time-sensitivity",
21        ],
22    },
23    Preset {
24        name: "moderation",
25        about: "Screen user content before it is published",
26        script: &[
27            ":state You people are useless. Fix my order or I'll come down there myself.",
28            ":noul is_threat The message threatens violence against a person | yes: A stated intent to harm someone | no: Anger, insults or profanity with no threat of harm",
29            ":score severity How far out of bounds this is | Fine as written | Rude but publishable | Abusive, needs review | Remove immediately",
30            ":choice action What to do with this content | publish=Nothing wrong with it | flag=A human should look | block=Clearly violates the rules",
31        ],
32    },
33    Preset {
34        name: "lead",
35        about: "Qualify an inbound sales email",
36        script: &[
37            ":state Hi — we're a 300-person logistics company evaluating vendors this quarter. Budget is approved. Can you send pricing for 250 seats?",
38            ":noul has_budget The sender indicates budget is available or approved",
39            ":score readiness How close this is to a buying decision | Just browsing | Researching options | Actively evaluating vendors | Ready to buy now",
40            ":choice size Company size implied by the message | smb=Under 50 people | mid=50 to 1000 people | enterprise=Over 1000 people",
41        ],
42    },
43    Preset {
44        name: "reply",
45        about: "Grade a draft reply before it is sent",
46        script: &[
47            ":state Draft reply: \"That's not our problem. You configured it wrong. Read the docs.\"",
48            ":noul answers_question The reply actually addresses what was asked",
49            ":score tone Tone of the reply | Warm and helpful | Neutral | Curt | Hostile",
50            ":noul safe_to_send This reply can go out without a human reading it first | yes: Accurate, on-topic and civil | no: Rude, evasive or likely to make things worse",
51        ],
52    },
53];
54
55pub fn find(name: &str) -> Option<&'static Preset> {
56    PRESETS.iter().find(|p| p.name == name)
57}
58
59/// A preset as the session it builds.
60///
61/// The scripts are REPL lines because that is how the REPL loads them; anything outside a terminal
62/// (the MCP server, the docs) wants the session, or the page [`crate::sketch::render`] makes of it.
63pub fn to_session(preset: &Preset) -> Session {
64    let mut built = Session::new();
65    for line in preset.script {
66        let (command, args) = match line.find(char::is_whitespace) {
67            Some(at) => (&line[..at], line[at + 1..].trim_start()),
68            None => (*line, ""),
69        };
70        if command == ":state" {
71            built.state = serde_json::Value::String(args.to_owned());
72            continue;
73        }
74        let parsed = match command {
75            ":noul" => session::parse_noul(args),
76            ":choice" => session::parse_choice(args),
77            ":score" => session::parse_score(args),
78            ":raw" => session::parse_raw(args),
79            _ => continue,
80        };
81        // A preset that does not parse is a bug in this file, not in the caller's input.
82        if let Ok((name, question)) = parsed {
83            built.insert(name, question);
84        }
85    }
86    built
87}