| 1 | //! The first-run notice copy. |
| 2 | //! |
| 3 | //! One string, owned by the crate that owns what is collected, so the TUI and |
| 4 | //! the CLI cannot drift into describing two different products. Every claim |
| 5 | //! below is checked against [`crate::event`] by a test: if the schema grows a |
| 6 | //! field this text does not cover, that test fails. |
| 7 | //! |
| 8 | //! Two properties of the wording are deliberate and load-bearing: |
| 9 | //! |
| 10 | //! 1. **The declining option is pre-selected and Enter takes it.** Enabling |
| 11 | //! requires a deliberate keystroke. There is no third option, no "improve |
| 12 | //! the product" checkbox, and nothing pre-checked. |
| 13 | //! 2. **The red lines are stated as "not collected", not as "anonymized".** |
| 14 | //! Sampling and hashing are not the same promise, and a notice that implies |
| 15 | //! them when neither is true is worse than no notice. |
| 16 | |
| 17 | /// Headline shown above [`NOTICE_BODY`]. |
| 18 | pub const NOTICE_HEADLINE: &str = "Help improve CodeWhale?"; |
| 19 | |
| 20 | /// The notice itself. |
| 21 | /// |
| 22 | /// Wrapped at 72 columns so it renders unchanged in a modal, in a pipe, and in |
| 23 | /// an 80-column terminal. |
| 24 | pub const NOTICE_BODY: &str = "\ |
| 25 | CodeWhale can send anonymous usage counts: which version you run, your |
| 26 | OS and CPU family, which features you used, how long sessions ran, and |
| 27 | how they ended. |
| 28 | |
| 29 | It never sends prompts, code, file names, paths, repo or branch names, |
| 30 | model output, model names, or credentials. Not sampled, not hashed — |
| 31 | not collected. |
| 32 | |
| 33 | You are identified only by a random ID stored on this machine. It is |
| 34 | deleted the moment you turn this off, and it is replaced every 90 days. |
| 35 | |
| 36 | Full schema, field by field: docs/TELEMETRY.md |
| 37 | Turn it off any time: codewhale config set telemetry false |
| 38 | or CODEWHALE_TELEMETRY=0"; |
| 39 | |
| 40 | /// The question, with the declining answer capitalised as the default. |
| 41 | pub const NOTICE_PROMPT: &str = "Enable telemetry? [y/N]"; |
| 42 | |
| 43 | /// The line printed once a decision is recorded, so the user has a receipt. |
| 44 | #[must_use] |
| 45 | pub fn decision_receipt(opt_in: bool) -> &'static str { |
| 46 | if opt_in { |
| 47 | "Telemetry is on. Turn it off any time with `codewhale config set telemetry false`." |
| 48 | } else { |
| 49 | "Telemetry stays off. You will not be asked again." |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | /// Whether a typed answer means yes. |
| 54 | /// |
| 55 | /// Everything else — an empty line, EOF, a closed pipe, `n`, a typo — means no. |
| 56 | /// That asymmetry is the point: only an affirmative answer is an affirmative |
| 57 | /// answer. |
| 58 | #[must_use] |
| 59 | pub fn answer_is_yes(input: &str) -> bool { |
| 60 | matches!(input.trim().to_ascii_lowercase().as_str(), "y" | "yes") |
| 61 | } |
| 62 |