Skip to main content

via/
skill.rs

1use crate::config::{CapabilityMode, Config};
2
3pub fn print(config: &Config) {
4    print!("{}", render(config));
5}
6
7pub fn render(config: &Config) -> String {
8    let mut output = String::new();
9    output.push_str("---\n");
10    output.push_str("name: via\n");
11    output.push_str("description: Use via when a task needs authenticated access to configured services without asking for or handling raw secrets. via resolves credentials from 1Password and runs configured capabilities such as REST API calls or delegated CLIs.\n");
12    output.push_str("---\n\n");
13    output.push_str("# via\n\n");
14    output.push_str("Use `via capabilities --json` before authenticated work to discover configured services and capabilities.\n\n");
15    output.push_str("Rules:\n");
16    output.push_str("- Never ask the user for tokens or passwords.\n");
17    output.push_str("- Never call the underlying secret provider directly.\n");
18    output
19        .push_str("- If provider authentication is not ready, ask the user to run `via login`.\n");
20    output.push_str("- Prefer REST capabilities because secrets stay inside `via`.\n");
21    output.push_str("- Use delegated capabilities only when the configured binary is trusted and its native behavior is required.\n");
22    output.push_str("- Do not print environment variables or credentials.\n");
23    output.push_str("- Run `via config doctor <service>` when a configured service fails.\n\n");
24    output.push_str("Configured capabilities:\n");
25
26    for (service_name, service) in &config.services {
27        output.push('\n');
28        match &service.description {
29            Some(description) => output.push_str(&format!("- `{service_name}`: {description}\n")),
30            None => output.push_str(&format!("- `{service_name}`\n")),
31        }
32        for (command_name, command) in &service.commands {
33            let usage = match command.mode() {
34                CapabilityMode::Rest => format!("via {service_name} {command_name} <path>"),
35                CapabilityMode::Delegated => {
36                    format!("via {service_name} {command_name} <tool-args...>")
37                }
38            };
39            match command.description() {
40                Some(description) => output.push_str(&format!(
41                    "  - `{command_name}`: {description} Use `{usage}`.\n"
42                )),
43                None => output.push_str(&format!("  - `{command_name}`: use `{usage}`.\n")),
44            }
45        }
46    }
47
48    output
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    fn config() -> Config {
56        Config::from_toml_str(
57            r#"
58version = 1
59
60[providers.onepassword]
61type = "1password"
62
63[services.github]
64description = "GitHub access"
65provider = "onepassword"
66
67[services.github.secrets]
68token = "op://Private/GitHub/token"
69
70[services.github.commands.api]
71description = "REST access."
72mode = "rest"
73base_url = "https://api.github.com"
74
75[services.github.commands.gh]
76description = "CLI access."
77mode = "delegated"
78program = "gh"
79"#,
80        )
81        .unwrap()
82    }
83
84    #[test]
85    fn renders_agent_rules_and_configured_capabilities() {
86        let output = render(&config());
87
88        assert!(output.contains("Never ask the user for tokens"));
89        assert!(output.contains("Never call the underlying secret provider directly"));
90        assert!(output.contains("via login"));
91        assert!(output.contains("via github api <path>"));
92        assert!(output.contains("via github gh <tool-args...>"));
93        assert!(!output.contains("op://Private"));
94        assert!(!output.contains("op read"));
95    }
96}