|
| 1 | +# 仓库透视 Repo X-Ray (`repo-xray`) |
| 2 | + |
| 3 | +English | [简体中文](README.zh-CN.md) |
| 4 | + |
| 5 | +Understand an unfamiliar codebase from its Git history before you read any code. Add a GitHub or |
| 6 | +GitLab repository, or pick one on your machine, and Repo X-Ray turns the commit log into a code map |
| 7 | +you can drill into, a short list of high-risk files, who maintains what, how the team ships, and |
| 8 | +what the project is built with. |
| 9 | + |
| 10 | +Analysis runs locally with your own `git`. Nothing is uploaded, and the page loads no remote |
| 11 | +resources. The metrics follow the rules of |
| 12 | +[codebase-recon-skill](https://github.com/yujiachen-y/codebase-recon-skill), and the Markdown export |
| 13 | +uses that skill's report format. The interface is in Chinese. |
| 14 | + |
| 15 | + |
| 16 | + |
| 17 | +*Preview uses synthetic data: a fictional monorepo generated for the screenshot.* |
| 18 | + |
| 19 | +## What you can do |
| 20 | + |
| 21 | +- **Overview**: key numbers, the main problems found by deterministic rules, recommendations, what |
| 22 | + changed since the previous analysis, and a small code map that links into the full one. |
| 23 | +- **Code map**: a treemap of the repository's directories that you can drill into. Colour it by |
| 24 | + change heat, bug-fix share, owner, last change, knowledge loss (the share of a directory's history |
| 25 | + written by people with no commits in the last 3 months), or file type. Size blocks by file count |
| 26 | + or by changes, and limit the window to 3, 6, or 12 months or all history. Works with the keyboard. |
| 27 | +- **High-risk files**: files that rank both as change hotspots and as bug magnets (most often |
| 28 | + touched by commits whose message mentions fix, bug, or broken), with their owners. |
| 29 | +- **Contributors**: ranking by non-merge commits, how concentrated the work is, who was active in |
| 30 | + the last 3 months, and bots and token accounts listed separately. Identities with the same name or |
| 31 | + email are merged by default; you can switch to one identity per author name. |
| 32 | +- **Rhythm and firefighting**: monthly commit trend, a commit calendar, and revert, hotfix, |
| 33 | + emergency, and rollback commits. |
| 34 | +- **Tech stack**: languages, frameworks, and tools detected from manifests such as `package.json`, |
| 35 | + `go.mod`, `Cargo.toml`, `pyproject.toml`, `pom.xml`, and `Gemfile`. |
| 36 | +- Search repositories, every file in history, and contributors with ⌘K. Copy or download the report |
| 37 | + as Markdown, or put a prepared prompt into the conversation input so the Agent reads the riskiest |
| 38 | + files for you; you review the text and send it yourself. |
| 39 | +- Suggestions of recently used local repositories, light and dark themes, keyboard shortcuts (press |
| 40 | + `?`), and `prefers-reduced-motion` support. |
| 41 | + |
| 42 | +## Requirements |
| 43 | + |
| 44 | +- `git` on `PATH`. Remote repositories need Git 2.31 or later, because credentials and the proxy are |
| 45 | + passed to git through `GIT_CONFIG_COUNT` environment variables. |
| 46 | +- Private repositories: if the `gh` (GitHub) or `glab` (GitLab) CLI is installed and logged in, the |
| 47 | + app reuses that login. Otherwise paste an access token for the host in Settings. |
| 48 | + |
| 49 | +## Install |
| 50 | + |
| 51 | +Copy this directory, including the hidden `.minimax-plugin/`, into the MiniMax Code plugins |
| 52 | +directory as `repo-xray/` (`~/.minimax/plugins/repo-xray/` by default; see the root README's Install |
| 53 | +section). Restart MiniMax Code, then open 仓库透视 from the Mini App entry in the right panel or ask |
| 54 | +the Agent to "打开仓库透视". |
| 55 | + |
| 56 | +## Tested environment |
| 57 | + |
| 58 | +- MiniMax Code 3.1.3-preview.4675 on macOS 27 with Git 2.54. Installed through the Host publish flow |
| 59 | + (also re-installed directly from this repository directory), opened in the right panel. |
| 60 | +- Exercised: a public GitHub repository, a self-hosted GitLab repository using the `glab` login, and |
| 61 | + local repositories, including re-analysis; every tab; every code-map colouring, size, and time |
| 62 | + option, drilling in and out with the mouse and the keyboard; the ⌘K search; the Markdown export; |
| 63 | + light and dark themes. No console errors or failed requests. |
| 64 | +- Not exercised: popping the page out into its own window (`surface.window`, sized 1360×900); the |
| 65 | + append into the conversation input, which asks the Host for confirmation; `prefers-reduced-motion`; |
| 66 | + Windows and Linux. |
| 67 | + |
| 68 | +## Data & access |
| 69 | + |
| 70 | +- Files read: |
| 71 | + - Repositories you add. A local repository is read in place with the read-only git commands |
| 72 | + below; the app only looks at the history of `HEAD`, never fetches, and never writes into it. |
| 73 | + - Local repository suggestions: lists directories under the scan roots (default `~/Workspace`, |
| 74 | + `~/workspace`, `~/code`, `~/Code`, `~/src`, `~/Projects`, `~/projects`, `~/Developer`, `~/dev`, |
| 75 | + `~/repos`, `~/GitHub`, `~/git`; up to 10 custom roots can be set in Settings), at most 4 levels |
| 76 | + deep, 4000 directories, and 300 repositories, skipping hidden directories and folders such as |
| 77 | + `node_modules`. For each directory it only checks for a `.git` entry and the modification times |
| 78 | + of `.git/FETCH_HEAD`, `.git/index`, and `.git/HEAD`. |
| 79 | + - The host names in `glab`'s config file (`$GLAB_CONFIG_DIR/config.yml`, |
| 80 | + `~/.config/glab-cli/config.yml`, or `~/Library/Application Support/glab-cli/config.yml`) and in |
| 81 | + `gh`'s `hosts.yml` (`$GH_CONFIG_DIR`, `$XDG_CONFIG_HOME/gh`, or `~/.config/gh`), to know which |
| 82 | + hosts these CLIs are logged in to. Tokens are not read from these files. |
| 83 | + - Whether `gh` and `glab` exist on `PATH`, `/opt/homebrew/bin`, `/usr/local/bin`, `/usr/bin`, |
| 84 | + `~/.local/bin`, or `~/.local/share/mise/shims`. |
| 85 | +- Files written, all inside the Host-provided `context.dataDir` (atomic write through a temporary |
| 86 | + file; JSON files are owner-only, mode `0600`): |
| 87 | + - `config.json`: the repository list and settings (scan roots, proxy, and any access tokens you |
| 88 | + paste in Settings, stored in plain text in this file). |
| 89 | + - `reports/<id>.json`: the analysis result for each repository. |
| 90 | + - `repos/<id>.git`: a bare, blob-less clone of each remote repository. It holds commits and trees, |
| 91 | + plus only the manifest and README-type files used for the tech stack and directory descriptions. |
| 92 | + - Removing a repository deletes its report and, for a remote repository, its clone. Local |
| 93 | + repositories are never modified. |
| 94 | +- Network: only `git` goes online, over HTTPS, to the host of each remote repository you add: clone |
| 95 | + and fetch, `ls-remote` to follow a renamed default branch, and fetching the few manifest and README |
| 96 | + files above. `git@host:path` and `ssh://` addresses are converted to HTTPS URLs; host names are |
| 97 | + validated first. If a proxy is set, git connects through it; the proxy comes from Settings, then |
| 98 | + `HTTPS_PROXY` / `https_proxy` / `HTTP_PROXY` / `http_proxy`, then the macOS system HTTPS proxy. |
| 99 | + The page loads only the app's own same-origin files and `/api/` routes. Links to repository pages |
| 100 | + on GitHub or GitLab open in your browser only when you click them. No telemetry. |
| 101 | +- Local server: it answers only requests whose `Host` is a loopback name and whose `Origin`, when |
| 102 | + sent, is the page itself; anything else gets `403`. |
| 103 | +- Processes, all started with an argument array and never through a shell. Addresses and paths you |
| 104 | + enter are validated and passed as separate arguments, never in an option position: |
| 105 | + - `git`: `clone --bare --filter=blob:none`, `fetch`, `ls-remote`, `log`, `ls-tree`, `cat-file`, |
| 106 | + `rev-parse`, `for-each-ref`, `symbolic-ref`, and `config` (reads `remote.origin.url` of a local |
| 107 | + repository to build web links; sets `remote.origin.fetch` and `HEAD` only inside the app's own |
| 108 | + clones). Inherited variables that would point git at another repository (`GIT_DIR` and the rest |
| 109 | + of `git rev-parse --local-env-vars`) or trace requests (`GIT_TRACE*`) are removed. |
| 110 | + - `gh auth token --hostname <host>` for `github.com` and hosts in `gh`'s `hosts.yml`, and |
| 111 | + `glab config get token --host <host>` for hosts in `glab`'s config, only when those CLIs are |
| 112 | + installed, to reuse your existing login. |
| 113 | + - `/usr/sbin/scutil --proxy` on macOS, only when no proxy is set in Settings or the environment. |
| 114 | +- Credentials: tokens from `gh`, `glab`, or Settings stay in the Node process. They reach git as an |
| 115 | + `http.extraHeader` through `GIT_CONFIG_*` environment variables, never in command-line arguments, |
| 116 | + repository config, the page, logs, or the event stream. While a token is attached, git does not |
| 117 | + follow HTTP redirects (`http.followRedirects=false`), so the token is only ever sent to the host |
| 118 | + you added; a repository that redirects (renamed or moved) has to be added again with its new |
| 119 | + address. Tokens from `gh` and `glab` are cached in memory only. Git credential helpers and |
| 120 | + interactive prompts are turned off for these commands. |
| 121 | +- Agent turns: none. No `agentAccess` and no MCP endpoints. "发到对话…", "让 Agent 讲解…", and |
| 122 | + "让 Agent 读高风险文件…" call `window.mcode.miniapp.message.append` to place a prepared prompt (a |
| 123 | + summary of the report and some file paths) in the conversation input after the Host asks you to |
| 124 | + confirm; you decide whether to send it. Where that API is unavailable, the text is copied to the |
| 125 | + clipboard instead. |
| 126 | +- Clipboard: written only when you click a copy action (a SHA, a path, the Markdown report). |
| 127 | +- Browser storage: `localStorage` keeps only the theme, the last opened repository, and the identity |
| 128 | + merge preference. |
| 129 | +- Reset: remove a repository with 移除 on its page; access tokens can be cleared in Settings. Deleting |
| 130 | + the app's data directory removes everything it stored. |
| 131 | + |
| 132 | +## Files |
| 133 | + |
| 134 | +```text |
| 135 | +.minimax-plugin/plugin.json Plugin manifest |
| 136 | +package.json Mini App declaration |
| 137 | +miniapp/miniapp.json Payload roots, Node entry, page route, pop-out window size |
| 138 | +miniapp/client/index.html Page shell served at /dashboard |
| 139 | +miniapp/client/app.js Page logic: routing, tabs, code map, dialogs (no dependencies) |
| 140 | +miniapp/client/charts.js Hand-written SVG charts and the treemap layout |
| 141 | +miniapp/client/styles.css Styles, light and dark themes |
| 142 | +miniapp/client/icon.svg App icon used inside the page |
| 143 | +miniapp/node/server.mjs Node entry: start(context) → { dispose }, routes, jobs, SSE |
| 144 | +miniapp/node/git.mjs git runner, address parsing, clone and fetch, credentials and proxy |
| 145 | +miniapp/node/analyze.mjs Reads the history and computes the report |
| 146 | +miniapp/node/codemap.mjs Code map: directory tree, colourings, directory descriptions |
| 147 | +miniapp/node/techstack.mjs Tech stack detection from manifests |
| 148 | +miniapp/node/util.mjs Date helpers, JSON read and atomic write |
| 149 | +miniapp/node/miniapp-api.ts Type declarations for the runtime context |
| 150 | +icon.png Plugin icon |
| 151 | +docs/preview.jpg README preview (synthetic data) |
| 152 | +``` |
| 153 | + |
| 154 | +## License |
| 155 | + |
| 156 | +[MIT](./LICENSE) |
0 commit comments