Skip to main content

oxicode/internal_urls/
mod.rs

1//! Internal URL scheme handlers for `issue://` and `pr://` virtual paths.
2//!
3//! Each handler implements [`oxicode_sdk::ports::ProtocolHandler`] and is
4//! registered with the `InternalUrlRouter` port so the `read`/`search`
5//! tools can resolve scheme URIs to markdown text.
6
7pub mod agent_handler;
8pub mod issue_handler;
9pub mod local_handler;
10pub mod memory_handler;
11pub mod pr_handler;
12pub mod rule_handler;
13pub mod skill_handler;
14/// Detect the current Git repo's `owner/repo` from the `origin` remote.
15///
16/// Returns `None` when `git remote get-url origin` fails or the URL is
17/// not a recognizable GitHub remote.
18pub(crate) fn detect_github_repo() -> Option<String> {
19    let output = std::process::Command::new("git")
20        .args(["remote", "get-url", "origin"])
21        .output()
22        .ok()?;
23    let url = String::from_utf8_lossy(&output.stdout).trim().to_string();
24    if url.is_empty() {
25        return None;
26    }
27    parse_github_owner_repo(&url)
28}
29
30/// Extract `owner/repo` from a GitHub remote URL.
31fn parse_github_owner_repo(url: &str) -> Option<String> {
32    // Handles:
33    //   https://github.com/owner/repo.git
34    //   https://github.com/owner/repo
35    //   git@github.com:owner/repo.git
36    //   git@github.com:owner/repo
37    //   ssh://git@github.com/owner/repo.git
38    let url = url.trim();
39
40    // Strip leading/trailing whitespace and newlines
41    let stripped = url
42        .strip_prefix("https://github.com/")
43        .or_else(|| url.strip_prefix("git@github.com:"))
44        .or_else(|| url.strip_prefix("ssh://git@github.com/"))?;
45
46    let stripped = stripped.strip_suffix(".git").unwrap_or(stripped);
47    let stripped = stripped.trim_end_matches('/');
48
49    // Validate owner/repo form
50    let parts: Vec<&str> = stripped.split('/').collect();
51    if parts.len() == 2 && !parts[0].is_empty() && !parts[1].is_empty() {
52        Some(format!("{}/{}", parts[0], parts[1]))
53    } else {
54        None
55    }
56}
57
58/// Get a GitHub token from environment (GITHUB_TOKEN or GH_TOKEN).
59pub(crate) fn github_token() -> Option<String> {
60    std::env::var("GITHUB_TOKEN")
61        .or_else(|_| std::env::var("GH_TOKEN"))
62        .ok()
63        .filter(|t| !t.is_empty())
64}