From d45cba368cf2cf5b3165c56757ca5a569677dbff Mon Sep 17 00:00:00 2001 From: Pheem49 Date: Sat, 20 Jun 2026 18:13:33 +0700 Subject: [PATCH 1/2] feat: add GitHub repository overview command and implement markdown heading formatting for CLI output --- README (Copy).md | 405 -------------------------- README.md | 2 + crates/mint-cli/src/agent.rs | 45 +++ crates/mint-cli/src/main.rs | 51 ++++ crates/mint-core/src/code_tools.rs | 95 ++++++ crates/mint-core/src/lib.rs | 2 +- crates/mint-core/src/orchestration.rs | 64 +++- 7 files changed, 253 insertions(+), 411 deletions(-) delete mode 100644 README (Copy).md diff --git a/README (Copy).md b/README (Copy).md deleted file mode 100644 index cfa82d9..0000000 --- a/README (Copy).md +++ /dev/null @@ -1,405 +0,0 @@ -# Mint - -

- Mint Icon -

- -

- Unified AI Desktop Assistant, Agentic CLI, and local-first automation workspace. -

- -

- License - Node.js - Electron - CLI Agentic - Documentation -

- -Mint is an AI assistant built to live in your desktop and terminal. It combines a transparent Electron desktop assistant, a unified agentic CLI, project-aware coding tools, local memory, automation, multi-provider AI routing, MCP extensions, and safety controls. - -## What's New - -- **Antigravity-style Desktop Layout:** Desktop UI now has a collapsible sidebar, Chat/Pictures navigation, smoother page transitions, startup loading polish, and clearer destructive-action confirmations. -- **Local Pictures Library:** Images sent from the desktop chat are saved locally under `~/.config/mint/Pictures` after the user sends the message, with an in-app Pictures gallery and local metadata index. -- **Image Privacy Hardening:** Chat history no longer stores raw image base64 data for saved images; history keeps a text placeholder while the actual file stays in the local Pictures folder. -- **Theme & UI Controls:** Settings now include theme, accent color, system text color, glass blur, font family, and font size controls that apply to the desktop interface. -- **Unified CLI Agent:** `mint` now routes every normal message through the same agent loop. It can think, answer conversationally, inspect projects, edit files, run tools, and finish directly for simple chat. -- **Fast Mode:** `/fast` switches the interactive CLI into a quieter `[Fast]` status that keeps the working indicator visible but hides internal `Thinking:` and tool-progress trace messages. -- **Live CLI Replies:** Mint responses now appear in one live-updating `Mint` message instead of waiting for the whole final answer to render at once. -- **Learned Skills:** `mint learn ` and `/learn ` import local `.md` or `.txt` files as persistent skill/instruction memory. Learned skills can be listed and deleted. -- **Provider Fallback:** The agent can fall back across supported providers, for example from local OpenAI-compatible backends to Gemini. -- **Provider Visibility:** Desktop and CLI responses show the provider/model that actually answered, including fallback results. -- **Live2D Assistant Model:** Desktop UI now supports a Live2D Shiroko model with expression cycling, lip sync while speaking, transparent interaction-area overlays, and click-to-chat reactions. -- **Google Workspace + Notion Integrations:** Gmail, Google Calendar, and Notion plugins can be configured from onboarding. -- **Safety Manager:** Central safety policy for shell commands and actions, including deterministic command blocking, permission tiers, path guards, and action logs. -- **Refactored Main Process:** Electron startup is split into focused modules for windows, IPC, proactive loop, screen capture, and action execution. -- **CI & Audit Baseline:** GitHub Actions runs install, tests, and security audit. Current local test baseline is `137` passing tests and `0` high vulnerabilities. -- **Dependency Hardening:** Removed vulnerable `google-tts-api` and `xlsx`; replaced with internal Google TTS URL generation and `read-excel-file`. - -## Installation & Setup - -### Quick Install (Recommended) - -The easiest way to install Mint CLI is using our installation script: - -**For macOS & Linux:** -```bash -curl -fsSL https://raw.githubusercontent.com/Pheem49/Mint/main/install.sh | bash -``` - -**For Windows (PowerShell or CMD):** -```powershell -powershell -Command "iwr -useb https://raw.githubusercontent.com/Pheem49/Mint/main/install.ps1 | iex" -``` - -### NPM Install - -If you prefer to install via NPM directly: - -```bash -npm install -g @pheem49/mint@latest -``` - -### Local Development - -```bash -git clone https://github.com/Pheem49/Mint.git -cd Mint -npm install -``` - -### Quick Start - -```bash -mint onboard -mint -npm start -``` - -Most integrations can be configured from: - -```bash -mint onboard -``` - -### Gmail - -Gmail uses Google OAuth, not a plain Gmail address/password. Configure the OAuth Client ID and Client Secret in onboarding, leave the refresh token empty if you do not have one yet, and keep `Gmail User ID` as `me` for the signed-in account. - -After onboarding, run one of: - -```bash -mint gmail auth -mint gmail auth --no-open -``` - -`mint gmail auth` opens the browser automatically. `mint gmail auth --no-open` prints the auth link for you to open manually. Both flows save `gmailRefreshToken` locally after Google redirects back to Mint. Recommended scopes are `gmail.readonly` and `gmail.compose`; Mint creates drafts only and does not send email automatically. - -### Google Calendar - -Google Calendar uses OAuth credentials and a refresh token. Onboarding stores: - -- `googleCalendarClientId` -- `googleCalendarClientSecret` -- `googleCalendarRefreshToken` -- `googleCalendarId`, usually `primary` - -The plugin can list events and create events through the Calendar API. If OAuth is not configured, it falls back to opening Google Calendar in the browser. - -### Notion - -Notion uses an internal integration secret. After creating an integration in Notion, share the target page or database with that integration, then configure: - -- `notionApiKey` -- `notionDatabaseId`, optional default database -- `notionPageId`, optional default page -- `notionTitleProperty`, default `Name` - -The plugin can create pages, query database pages, and append text blocks. - -## Key Features - -
-Show details - -### Unified CLI Agent - -Mint CLI is not just a chat wrapper. It is a workspace-aware agent loop. - -- **Think Before Acting:** Every request goes through an agent decision step. -- **Fast Mode:** Toggle `/fast` to hide internal thought/progress messages while keeping the final answer, approvals, tools, and working indicator unchanged. -- **Live Answer Rendering:** Final answers are streamed into a single Mint message block as they arrive. -- **Conversational + Coding in One Flow:** Casual messages can finish directly; coding tasks can inspect, plan, edit, and verify. -- **Workspace Context:** Reads current path, git status, diff summary, package scripts, and previous workspace session memory. -- **Tool Use:** Supports web search, file listing, file reading, scoped code search, path finding, shell commands, patch edits, file writes, opening folders, and asking the user. -- **Approval Flow:** Shell commands, patches, and full-file writes require user approval. -- **Provider Support:** Gemini, OpenAI, Anthropic, and local OpenAI-compatible endpoints for agent tasks. -- **Agent Collaboration Option:** Optional reviewer pass can be enabled for longer tasks. - -### Desktop Assistant - -- **Electron Desktop UI:** Transparent desktop assistant window with tray support. -- **Collapsible Workspace Sidebar:** Desktop navigation starts collapsed on app launch and can be expanded for Chat, Pictures, model controls, and settings. -- **Chat Navigation Safety:** `New Chat` and `Clear` ask for confirmation before clearing the current conversation history. -- **Pictures Gallery:** Sent images are available in a local-only Pictures view inside the desktop app. -- **Live2D Model View:** Optional Live2D assistant panel with model show/hide persistence. New installs start with the model hidden until the user enables it. -- **Live2D Expressions:** Cycle model expressions from the toolbar and show an on-canvas expression toast for the active expression. -- **Click Reactions:** Named model interaction zones (`Head Pat`, `Cheek Poke`, `Hand Tap`, `Shoulder Tap`, and `Careful`) can trigger temporary expressions and send short contextual prompts into the normal chat flow. -- **Interaction Guide Overlay:** Toggle a transparent overlay that labels clickable model areas without blocking pointer input. -- **Voice Lip Sync:** When Mint speaks, Live2D mouth parameters animate during TTS playback and reset when speech ends. -- **Floating Widget:** Always-on-top quick access widget. -- **Spotlight Launcher:** `Alt+Space` quick prompt window. -- **Screen Vision:** Capture the screen and send selected regions to the AI. -- **Live Translation:** Continuously translate a selected screen area. -- **Proactive Suggestions:** Periodic screen/context analysis with behavior memory. -- **System Notifications:** Low battery, connection changes, and proactive notices. -- **Settings UI:** Configure provider, model, theme, keys, bridge options, MCP, and assistant behavior. -- **Appearance Controls:** Customize theme, accent color, text color, glass blur, font family, and UI font size. - -### Automation - -- **Apps and Websites:** Open local apps, URLs, search queries, files, and folders. -- **Browser Automation:** Use Puppeteer-driven browser workflows. -- **File Operations:** Create folders, find paths, open files/folders, and move files to trash. -- **System Automation:** Volume, mute, brightness, suspend, restart, shutdown, and window minimization helpers. -- **Granular Automation:** Mouse move, mouse click, typing, and key tap actions. -- **Custom Workflows:** Process-monitoring rules loaded from local config. -- **Headless Agent:** Queue background tasks with `mint task`. - -### Knowledge and Memory - -- **Chat History:** Persistent local chat transcript. -- **Timestamp Preservation:** Desktop chat history keeps original message timestamps across app restarts and history syncs. -- **Local Sent-Image Storage:** Desktop image attachments are saved as local files only after sending, under `~/.config/mint/Pictures`. -- **Behavior Memory:** Stores recurring user context for proactive suggestions. -- **Long-Term Memory Store:** SQLite-backed user context, session memories, usage patterns, and response cache. -- **Learned Skill Files:** Import `.md` or `.txt` instruction files with `mint learn ` or `/learn `. Mint remembers them as persistent skill/instruction context. -- **Knowledge Base / RAG:** Index and search local `.txt`, `.md`, `.pdf`, `.docx`, and `.xlsx` files. -- **Workspace Session Memory:** Remembers previous task summary and verification for each workspace. - -### Multi-Provider AI - -- **Gemini:** Main default provider with model selection. -- **OpenAI:** GPT-compatible cloud provider. -- **Anthropic:** Claude-compatible provider. -- **Local OpenAI Compatible:** LM Studio or other local `/v1/chat/completions` servers. -- **Ollama / Hugging Face:** Available in general provider configuration where supported. -- **Fallback Routing:** Agent provider selection can fall back when local providers are offline. -- **Response Badges:** Chat surfaces show the provider/model that produced the final response, such as `gemini • gemini-3.1-flash-lite-preview`. - -### Messaging Bridges and Plugins - -- **Discord Bridge** -- **Telegram Bridge** -- **Slack Bridge** -- **LINE Bridge** -- **WhatsApp Bridge** -- **Google Search and Brave Search Bridges** -- **Spotify Plugin** -- **Docker Plugin** -- **Obsidian Plugin** -- **System Monitor and Metrics Plugins** -- **Google Calendar Plugin:** List events and create calendar events via Google Calendar API, with browser fallback. -- **Gmail Plugin:** Search/read Gmail and create drafts safely. It does not send email automatically. -- **Notion Plugin:** Create notes/pages, read databases, and append page blocks through the Notion API. -- **MCP Manager** - -### MCP Extensions - -Mint supports the **Model Context Protocol (MCP)** so external tools can be added without hardcoding them into Mint. - -```bash -mint mcp add --args --env -mint mcp list -mint mcp remove -mint mcp clear -``` - -Example: - -```bash -mint mcp add google-search npx --args -y @modelcontextprotocol/server-google-search --env GOOGLE_API_KEY=your_key GOOGLE_SEARCH_ENGINE_ID=your_id -``` - -
- -## Safety System - -
-Show details - -Mint includes a central safety layer in `src/System/safety_manager.js`. - -- **Permission Tiers:** `safe`, `approval`, `dangerous`, and `blocked`. -- **Deterministic Command Blocking:** Blocks known dangerous shell commands regardless of what the AI requests. -- **Blocked Examples:** `rm -rf`, `git reset --hard`, `git clean -f`, `mkfs`, raw disk writes, `shutdown`, `reboot`, `sudo`, `chmod -R 777`, `curl | sh`, and `wget | bash`. -- **Dangerous Actions:** `delete_file` and destructive `system_automation` actions require explicit permission. -- **Path Guard:** Prevents path traversal outside an allowed root. -- **Action Logs:** Writes JSONL records to `~/.config/mint/action-log.jsonl`. -- **Test Coverage:** Safety tests verify destructive command blocking, dangerous action classification, path traversal protection, and action executor enforcement. - -
- -## Screenshots - -
-Show screenshots - -

- Mint Desktop UI - Mint Settings -
- Desktop Assistant UI with Live2D model, chat panel, sidebar navigation, and local Pictures view. Settings UI for providers, automation, theme, voice, plugins, and MCP configuration. -

- -

- Mint CLI -
- Unified CLI Agent for chat, coding tasks, tool use, workspace context, image input, and command workflows. -

- -
- -## CLI Commands - -
-Show details - -- `mint` / `mint chat` - Start the unified interactive agent UI. -- `mint chat ""` - Start with an initial message. -- `mint chat --image ./screenshot.png "What is on this screen?"` - Attach an image to the initial chat message. -- `/image ./screenshot.png What is on this screen?` - Attach an image while inside the interactive CLI, then press Enter to send. -- `Ctrl+V` or `/paste What is on this screen?` - Attach clipboard images inside the interactive CLI, then press Enter to send. -- `mint learn ./skill.md` - Read a local `.md` or `.txt` file and remember it as a persistent Mint skill/instruction. -- `mint learn --list` - List learned skill files. -- `mint learn --delete ` - Delete a learned skill by ID, path, or file name. -- `mint summarize [path]` - Summarize repository structure, package metadata, git state, and key files. -- `mint symbols [path]` - Build a local source symbol index for supported code files. -- `mint semantic-code index [path]` - Create embeddings for local source code chunks. -- `mint semantic-code search ""` - Search the indexed code semantically. -- `mint code ""` - Run a specific coding task in the current workspace. -- `mint code --image ./mockup.png "Build this UI"` - Attach an image as visual context for a coding task. -- `mint gmail auth` - Open Google OAuth and save a Gmail refresh token. -- `mint gmail auth --no-open` - Print the Gmail OAuth link without opening a browser. -- `mint task ""` - Queue a background task for the headless agent. -- `mint agent [task]` - Run the background/headless agent. -- `mint mcp` - Manage MCP servers. -- `mint update` - Check npm and install the latest Mint CLI version. -- `mint update --check` - Check for a newer version without installing it. -- `mint list` - Display available features and commands. -- `mint onboard` - Configure Mint for first use. - -
- -## CLI Updates - -
-Show details - -Mint CLI checks for updates automatically on startup. The auto-check is enabled by default, uses a 24-hour cooldown, and updates from npm with `npm install -g @pheem49/mint@latest` when a newer package version is available. - -Use manual update commands when you want direct control: - -```bash -mint update -mint update --check -mint update --dry-run -``` - -You can skip the startup auto-check for one command: - -```bash -MINT_SKIP_AUTO_UPDATE=1 mint -``` - -To disable automatic update checks, set `enableAutoUpdate` to `false` in your Mint config file. - -
- -## Interactive Slash Commands - -
-Show details - -Inside `mint`: - -- `/help` - Show commands. -- `/fast [on|off|status]` - Toggle Fast Mode. Fast Mode shows `[Fast]`, keeps `Mint is thinking...`, and hides `Thinking:`/progress trace messages. -- `/summarize [path] [--json]` - Summarize the current repository without using the AI agent. -- `/symbols [path] [--json] [--limit n]` - Build a local source symbol index without using the AI agent. -- `/semantic-code index` - Create embeddings for source code chunks in the current workspace. -- `/semantic-code search ` - Search the indexed code semantically. -- `/learn ` - Read a local `.md` or `.txt` file and remember it as a persistent Mint skill/instruction. -- `/memory skills` - Show learned skill files. -- `/memory skills delete ` - Delete a learned skill. -- `/image [prompt]` - Attach an image from disk. -- `/paste [prompt]` - Attach an image from the clipboard. -- `/code ` - Force Code Mode. -- `/cd ` - Change active workspace directory. -- `/models [name]` - Show or switch model/provider. -- `/memory [cmd]` - Manage long-term memory. -- `/config` - Show current configuration. -- `/copy` - Copy last response. -- `/clear` / `/reset` - Clear conversation history. -- `/agent ` - Switch specialized persona. -- `/workspace` - Manage registered workspaces. -- `/stats` - Show system statistics. -- `/review` - Ask reviewer persona to critique the last answer. -- `/exit` - Exit. - -
- -## Development - -
-Show details - -```bash -npm test -npm test -- --runInBand -npm audit --audit-level=high -npm start -npm run build:linux -``` - - -## Runtime Notes - -
-Show details - -- Mint is currently a **Node.js + CommonJS** project, not TypeScript. -- API keys are stored locally in Mint config or environment variables. -- Google OAuth refresh tokens for Gmail and Calendar are stored locally in Mint config. -- Desktop chat images sent by the user are stored locally in `~/.config/mint/Pictures`, with metadata in `~/.config/mint/Pictures/pictures.json`. -- Desktop chat history is stored locally in `~/.config/mint/mint-chat-history.json`. -- Local OpenAI-compatible providers require a running local server such as LM Studio. -- Some desktop features depend on Linux tools such as `xdg-open`, `gio`, `xdotool`, `amixer`, `pactl`, `brightnessctl`, or `xbacklight`. -- Electron GUI behavior should be smoke-tested manually after large UI or main-process changes. - -
- -## Security & Privacy - -
-Show details - -- **Local Control:** Mint prioritizes local execution and local configuration. -- **Local Picture Storage:** Desktop images are saved only on the user's machine, under `~/.config/mint/Pictures`, after the user sends a message with an image. -- **No Raw Image History:** Saved desktop images are omitted from chat history as raw base64 and replaced with a text placeholder. -- **User Approval:** Shell commands, patches, and file writes require explicit approval in the CLI agent. -- **Safety Manager:** Dangerous commands and actions are blocked or gated by deterministic policy. -- **Action Audit Trail:** Tool actions are logged locally for debugging and accountability. -- **Secure Config Practice:** Keys stay on the user's machine and are only sent to the selected AI/search provider. - -
- -## License - -Mint is licensed under the **GNU Affero General Public License v3.0**. -See the [LICENSE](LICENSE) file for details. - ---- - -

Made with love by Pheem49

diff --git a/README.md b/README.md index 82263b0..eccacfd 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Mint is a local-first AI assistant running on your machine, capable of handling ### 5. 🔌 Tool & MCP Integrations - Support **Model Context Protocol (MCP)** to connect tools like Google/Brave Search, Filesystem servers, and GitHub context. +- **Auto GitHub Link Resolver:** Automatically detects GitHub URLs in chat messages (CLI, Web, and Desktop) and Code Agent tasks. It fetches and injects the repository's metadata, directory structure, and README as prompt context, serving as an instant fallback when the GitHub MCP server is not active. - Local plugins for Spotify playback control, Google Calendar, Gmail drafts, and Notion workspace reading. --- @@ -307,6 +308,7 @@ Mint includes native workspace tools for code inspection, planning, editing, and ```bash mint code agent "inspect this repo and fix the failing tests" +mint code github-overview "Pheem49/Mint" mint code summary . mint code search "shell approval flow" . mint symbols . diff --git a/crates/mint-cli/src/agent.rs b/crates/mint-cli/src/agent.rs index 4fdbf1e..e707a0a 100644 --- a/crates/mint-cli/src/agent.rs +++ b/crates/mint-cli/src/agent.rs @@ -785,6 +785,51 @@ fn confirm_pausing_interrupt(prompt: &str, approval_active: &AtomicBool) -> bool } fn format_markdown_bold(text: &str) -> String { + let mut formatted_lines = Vec::new(); + let mut in_code_block = false; + + for line in text.lines() { + let mut formatted_line = line.to_string(); + let trimmed = line.trim_start(); + + if trimmed.starts_with("```") { + in_code_block = !in_code_block; + } + + if !in_code_block { + let hash_count = trimmed.chars().take_while(|&c| c == '#').count(); + if hash_count > 0 && hash_count <= 6 { + let next_char = trimmed.chars().nth(hash_count); + let is_heading = match next_char { + Some(' ') => true, + Some(c) => !c.is_alphanumeric(), + None => true, + }; + if is_heading { + // Make the entire heading line bold and bright white + formatted_line = format!("{}{}{}", BRIGHT, line, RESET); + } else { + formatted_line = process_inline_bold(&formatted_line); + } + } else { + formatted_line = process_inline_bold(&formatted_line); + } + } else { + // Do not format markdown inside code blocks + formatted_line = line.to_string(); + } + + formatted_lines.push(formatted_line); + } + + let mut result = formatted_lines.join("\n"); + if text.ends_with('\n') { + result.push('\n'); + } + result +} + +fn process_inline_bold(text: &str) -> String { let count = text.matches("**").count(); let pair_limit = (count / 2) * 2; let mut result = String::with_capacity(text.len()); diff --git a/crates/mint-cli/src/main.rs b/crates/mint-cli/src/main.rs index 9dfeab3..28e593e 100644 --- a/crates/mint-cli/src/main.rs +++ b/crates/mint-cli/src/main.rs @@ -14,6 +14,7 @@ use mint_core::{ load_config, native_plugins, orchestrate_chat_stream_with_fallback, orchestrate_chat_with_fallback, propose_code_edits, read_code_file, repository_summary, run_shell_command, search_code, search_semantic_code, set_config_value, + parse_github_url, fetch_github_repo_summary, }; mod agent; @@ -400,6 +401,11 @@ enum CodeCommand { #[arg(long, default_value = ".")] root: PathBuf, }, + /// Fetch GitHub repository metadata and README, then get an overview of the repo. + GithubOverview { + /// The GitHub repository URL or name (e.g. "https://github.com/owner/repo" or "owner/repo"). + repo: String, + }, } #[derive(Debug, Subcommand)] @@ -895,6 +901,9 @@ async fn main() -> Result<()> { &config, )?)? ), + CodeCommand::GithubOverview { repo } => { + run_github_overview(&repo, &config).await?; + } } } Command::Open { target } => { @@ -2737,6 +2746,48 @@ fn print_shell_output(output: &mint_core::ShellOutput) { ); } + + +async fn run_github_overview(repo: &str, config: &MintConfig) -> Result<()> { + let Some((owner, repo_name)) = parse_github_url(repo) else { + anyhow::bail!("Invalid GitHub repository URL/format. Please use 'owner/repo' or a full GitHub URL."); + }; + + println!("Fetching information for {}/{} from GitHub...", owner, repo_name); + let summary = match fetch_github_repo_summary(&owner, &repo_name).await { + Ok(s) => s, + Err(e) => { + anyhow::bail!("Failed to fetch repository summary: {}. Check that the repository is public and spelled correctly.", e); + } + }; + + println!("Analyzing repository with AI model..."); + let prompt = format!( + "Here is the metadata, top-level directory structure, and README.md content for the GitHub repository {}/{}:\n\n{}\n\nBased on this information, please provide a high-level overview of what this repository is about, what tech stack it uses, its overall architecture, and how it is organized.", + owner, repo_name, summary + ); + + let (response, _) = orchestrate_chat_with_fallback( + config, + &ChatRequest { + message: prompt, + system_instruction: "You are a professional software architect providing a high-level overview of a code repository based on its metadata and README.".to_string(), + chat_id: Some("github_review".to_string()), + image_data_uri: None, + audio_data_uri: None, + document_attachment: None, + workspace_path: None, + }, + ) + .await?; + + println!("\n--- AI Repository Overview for {}/{} ---", owner, repo_name); + println!("{}", response.text); + println!("--------------------------------------------------"); + Ok(()) +} + + #[cfg(test)] mod tests { use super::*; diff --git a/crates/mint-core/src/code_tools.rs b/crates/mint-core/src/code_tools.rs index 08777e4..5a8f2d1 100644 --- a/crates/mint-core/src/code_tools.rs +++ b/crates/mint-core/src/code_tools.rs @@ -465,6 +465,101 @@ fn is_ignored_directory(path: &Path) -> bool { .is_some_and(|name| IGNORED_DIRECTORIES.contains(&name)) } +pub fn parse_github_url(url: &str) -> Option<(String, String)> { + let cleaned = url.trim() + .trim_start_matches("https://") + .trim_start_matches("http://") + .trim_start_matches("www.") + .trim_start_matches("github.com/"); + + let parts: Vec<&str> = cleaned.split('/').collect(); + if parts.len() >= 2 { + let owner = parts[0].to_string(); + let mut repo = parts[1].to_string(); + if repo.ends_with(".git") { + repo = repo[..repo.len() - 4].to_string(); + } + Some((owner, repo)) + } else { + None + } +} + +pub async fn fetch_github_repo_summary(owner: &str, repo: &str) -> Result { + let client = reqwest::Client::builder() + .user_agent("mint-core") + .build() + .map_err(|e| e.to_string())?; + + // 1. Fetch Repository Info + let repo_url = format!("https://api.github.com/repos/{}/{}", owner, repo); + let repo_resp = client.get(&repo_url).send().await.map_err(|e| e.to_string())?; + if !repo_resp.status().is_success() { + return Err(format!("Failed to fetch repository metadata: {}", repo_resp.status())); + } + let repo_info: serde_json::Value = repo_resp.json().await.map_err(|e| e.to_string())?; + + let description = repo_info["description"].as_str().unwrap_or("No description provided."); + let language = repo_info["language"].as_str().unwrap_or("Unknown"); + let stars = repo_info["stargazers_count"].as_u64().unwrap_or(0); + let forks = repo_info["forks_count"].as_u64().unwrap_or(0); + + let mut topics_list = Vec::new(); + if let Some(topics) = repo_info["topics"].as_array() { + for t in topics { + if let Some(t_str) = t.as_str() { + topics_list.push(t_str.to_string()); + } + } + } + let topics_str = if topics_list.is_empty() { + "None".to_string() + } else { + topics_list.join(", ") + }; + + // 2. Fetch Directory contents (top level) + let contents_url = format!("https://api.github.com/repos/{}/{}/contents", owner, repo); + let contents_resp = client.get(&contents_url).send().await.map_err(|e| e.to_string())?; + let mut file_tree = String::from("Unavailable"); + if contents_resp.status().is_success() { + if let Ok(contents_info) = contents_resp.json::().await { + if let Some(arr) = contents_info.as_array() { + let mut files = Vec::new(); + for item in arr { + let name = item["name"].as_str().unwrap_or(""); + let r#type = item["type"].as_str().unwrap_or(""); + files.push(format!("- {} ({})", name, r#type)); + } + file_tree = files.join("\n"); + } + } + } + + // 3. Fetch README.md + let readme_url = format!("https://api.github.com/repos/{}/{}/readme", owner, repo); + let readme_resp = client.get(&readme_url).send().await.map_err(|e| e.to_string())?; + let mut readme_text = String::from("No README available."); + if readme_resp.status().is_success() { + if let Ok(readme_info) = readme_resp.json::().await { + if let Some(content_b64) = readme_info["content"].as_str() { + let cleaned_b64 = content_b64.replace('\n', "").replace('\r', ""); + use base64::{Engine as _, engine::general_purpose::STANDARD}; + if let Ok(decoded_bytes) = STANDARD.decode(cleaned_b64) { + readme_text = String::from_utf8_lossy(&decoded_bytes).to_string(); + } + } + } + } + + let summary = format!( + "Repository: {}/{}\nDescription: {}\nPrimary Language: {}\nStars: {}\nForks: {}\nTopics: {}\n\nTop-level File Directory:\n{}\n\nREADME.md:\n{}", + owner, repo, description, language, stars, forks, topics_str, file_tree, readme_text + ); + + Ok(summary) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/mint-core/src/lib.rs b/crates/mint-core/src/lib.rs index 0dbdd8b..49fda02 100644 --- a/crates/mint-core/src/lib.rs +++ b/crates/mint-core/src/lib.rs @@ -32,7 +32,7 @@ pub use code_tools::{ AppliedCodeEdit, CodeEdit, CodeEditPreview, CodeEditProposal, CodeFile, CodeInspectionError, CodePatchHunk, CodePlan, CodeSearchHit, RepositorySummary, apply_code_edits, build_code_patch, inspect_code_plan, list_code_files, propose_code_edits, read_code_file, repository_summary, - search_code, + search_code, parse_github_url, fetch_github_repo_summary, }; pub use config::{ ConfigError, MintConfig, config_path, initialize_config, load_config, save_config, diff --git a/crates/mint-core/src/orchestration.rs b/crates/mint-core/src/orchestration.rs index a73ad06..76c50d8 100644 --- a/crates/mint-core/src/orchestration.rs +++ b/crates/mint-core/src/orchestration.rs @@ -35,12 +35,59 @@ pub enum OrchestrationError { Agent(String), } +pub async fn resolve_github_links(message: &str, config: &MintConfig) -> String { + // Check if a GitHub MCP server is configured in Settings + let github_mcp_configured = crate::mcp::configured_mcp_servers(config) + .ok() + .map(|servers| servers.contains_key("github")) + .unwrap_or(false); + + if github_mcp_configured { + // If GitHub MCP is active, we let it handle the repo via tool calls + // to avoid duplicate/redundant context. + return message.to_string(); + } + + use std::sync::OnceLock; + static RE: OnceLock = OnceLock::new(); + let re = RE.get_or_init(|| { + regex::Regex::new(r"https?://(?:www\.)?github\.com/([a-zA-Z0-9\-_.]+)/([a-zA-Z0-9\-_.]+)").unwrap() + }); + + let mut resolved_msg = message.to_string(); + let mut resolved_repos = std::collections::HashSet::new(); + + for caps in re.captures_iter(message) { + if let (Some(owner_match), Some(repo_match)) = (caps.get(1), caps.get(2)) { + let owner = owner_match.as_str(); + let mut repo = repo_match.as_str().to_string(); + if repo.ends_with(".git") { + repo = repo[..repo.len() - 4].to_string(); + } + let repo_clean: String = repo.chars().take_while(|c| c.is_alphanumeric() || *c == '-' || *c == '_' || *c == '.').collect(); + + let repo_key = format!("{owner}/{repo_clean}"); + if resolved_repos.insert(repo_key.clone()) { + if let Ok(summary) = crate::code_tools::fetch_github_repo_summary(owner, &repo_clean).await { + resolved_msg.push_str(&format!( + "\n\n--- Auto-Resolved GitHub Metadata for {} ---\n{}\n--------------------------------------------", + repo_key, summary + )); + } + } + } + } + resolved_msg +} + pub async fn orchestrate_chat( config: &MintConfig, request: &ChatRequest, ) -> Result { + let mut resolved_request = request.clone(); + resolved_request.message = resolve_github_links(&request.message, config).await; let memory = MemoryStore::open_default()?; - let enriched = enrich_request(&memory, request)?; + let enriched = enrich_request(&memory, &resolved_request)?; let response = send_chat(config, &enriched).await?; memory.add_interaction_for_chat_with_fallback( request_chat_id(request), @@ -66,8 +113,10 @@ pub async fn orchestrate_chat_stream( where F: FnMut(String), { + let mut resolved_request = request.clone(); + resolved_request.message = resolve_github_links(&request.message, config).await; let memory = MemoryStore::open_default()?; - let enriched = enrich_request(&memory, request)?; + let enriched = enrich_request(&memory, &resolved_request)?; let response = stream_chat(config, &enriched, on_chunk).await?; memory.add_interaction_for_chat_with_fallback( request_chat_id(request), @@ -89,8 +138,10 @@ pub async fn orchestrate_chat_with_fallback( config: &MintConfig, request: &ChatRequest, ) -> Result<(ChatResponse, Option), OrchestrationError> { + let mut resolved_request = request.clone(); + resolved_request.message = resolve_github_links(&request.message, config).await; let memory = MemoryStore::open_default()?; - let enriched = enrich_request(&memory, request)?; + let enriched = enrich_request(&memory, &resolved_request)?; let (response, fallback) = send_chat_with_fallback(config, &enriched).await?; memory.add_interaction_for_chat_with_fallback( request_chat_id(request), @@ -116,8 +167,10 @@ pub async fn orchestrate_chat_stream_with_fallback( where F: FnMut(String), { + let mut resolved_request = request.clone(); + resolved_request.message = resolve_github_links(&request.message, config).await; let memory = MemoryStore::open_default()?; - let enriched = enrich_request(&memory, request)?; + let enriched = enrich_request(&memory, &resolved_request)?; let (response, fallback) = stream_chat_with_fallback(config, &enriched, on_chunk).await?; memory.add_interaction_for_chat_with_fallback( request_chat_id(request), @@ -572,8 +625,9 @@ where e )) })?; + let resolved_task = resolve_github_links(task, config).await; let skills = crate::skills::learned_skills_context().unwrap_or_default(); - let mut observation = initial_observation(task, &root, &skills); + let mut observation = initial_observation(&resolved_task, &root, &skills); let mut pending_image = image_data_uri; let mut system_prompt = build_system_prompt(config); From 7b84b4e104720170e3a86db168691508098eb24d Mon Sep 17 00:00:00 2001 From: Pheem49 Date: Sat, 20 Jun 2026 20:27:17 +0700 Subject: [PATCH 2/2] fix: remove non-functional open folder button from pictures library header --- src/renderer/src-web/components/PicturesLibrary.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/renderer/src-web/components/PicturesLibrary.tsx b/src/renderer/src-web/components/PicturesLibrary.tsx index 7757155..4b10255 100644 --- a/src/renderer/src-web/components/PicturesLibrary.tsx +++ b/src/renderer/src-web/components/PicturesLibrary.tsx @@ -46,9 +46,6 @@ export default function PicturesLibrary({ view, pictures, onSetView }: PicturesL
Gallery

Saved Pictures

-
{pictures.length === 0 ? (