1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//! Cross-surface strings for the cloud runtime-coverage path.
//!
//! The cloud commands of `fallow coverage` and the matching MCP cloud tools
//! refuse the same call for the same reason, and an agent reads both.
//! Keeping the refusal text here makes the two surfaces say one sentence
//! instead of holding two copies that drift apart.
/// A cloud command that needs a Fallow Cloud API key. The refusal names the
/// command that ran, so the example line can be copied as it is.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CloudCommand {
/// `fallow coverage analyze --cloud`, and the MCP `get_cloud_runtime_context` tool.
Analyze,
/// `fallow coverage review-packet`, and the MCP `get_cloud_review_packet` tool.
ReviewPacket,
/// `fallow coverage deployment-changes`, and the MCP `get_cloud_deployment_changes` tool.
DeploymentChanges,
}
impl CloudCommand {
/// A minimal command line for this command, without the API key.
#[must_use]
pub const fn example(self) -> &'static str {
match self {
Self::Analyze => "fallow coverage analyze --cloud --repo owner/repo",
Self::ReviewPacket => "fallow coverage review-packet --repo owner/repo",
Self::DeploymentChanges => "fallow coverage deployment-changes --repo owner/repo",
}
}
}
/// Refusal emitted when no Fallow Cloud API key is available. Names the
/// environment variable first because that is the only route the MCP tools
/// have; the flag and the command line of `command` follow for the CLI surface.
#[must_use]
pub fn cloud_api_key_missing_message(command: CloudCommand) -> String {
format!(
"Cloud runtime coverage requires an API key.\n\nSet FALLOW_API_KEY or pass --api-key:\n\n FALLOW_API_KEY=fallow_live_... {}",
command.example()
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn each_command_names_itself_in_the_example() {
for (command, subcommand) in [
(CloudCommand::Analyze, "coverage analyze --cloud"),
(CloudCommand::ReviewPacket, "coverage review-packet"),
(
CloudCommand::DeploymentChanges,
"coverage deployment-changes",
),
] {
let message = cloud_api_key_missing_message(command);
assert!(message.starts_with("Cloud runtime coverage requires an API key."));
assert!(
message.ends_with(&format!(
"fallow_live_... fallow {subcommand} --repo owner/repo"
)),
"{command:?}: {message}"
);
}
}
}