Skip to content

feat: support multi-plugin repos with subpath in git URLs#1529

Merged
tempurai merged 4 commits intomainfrom
feat/multi-plugin-repo
Mar 20, 2026
Merged

feat: support multi-plugin repos with subpath in git URLs#1529
tempurai merged 4 commits intomainfrom
feat/multi-plugin-repo

Conversation

@tempurai
Copy link
Copy Markdown
Collaborator

@tempurai tempurai commented Mar 20, 2026

Summary

  • Add _parse_git_url() to parse git URLs into (clone_url, subpath), supporting .git URLs, SSH, GitHub/GitLab short URLs, and browser tree/{branch}/ URLs
  • When subpath is specified, install the plugin from that subdirectory after cloning
  • When no subpath is given and repo root has no plugin.json, scan subdirectories and suggest available plugins with the correct install URL
  • Path traversal protection via is_relative_to() on resolved subpath

Supported URL formats

Format Example
.git + subpath https://github.com/org/repo.git/my-plugin
SSH + subpath git@github.com:org/repo.git/my-plugin
GitHub short URL https://github.com/org/repo/my-plugin
Browser URL https://github.com/org/repo/tree/main/my-plugin

Scan-and-suggest example

$ kimi plugin install https://github.com/MoonshotAI/kimi-cli-plugins.git
Error: No plugin.json at repository root. Available plugins:
  - sample-plugin
  - stock-assistant
Use: kimi plugin install <url>/<plugin-name>

Test plan

  • 17 parametrized tests for _parse_git_url (all URL formats + edge cases)
  • 7 integration tests for _resolve_source git subpath (with mocked git clone)
  • All 57 existing plugin tests pass (no regressions)
  • E2E tested against real GitHub repo (MoonshotAI/kimi-cli-plugins)
  • Lint + type check clean

Open with Devin

Add _parse_git_url() to split git URLs into (clone_url, subpath),
supporting .git URLs, SSH, GitHub/GitLab short URLs, and browser
tree/{branch}/ URLs. When no subpath is given and root has no
plugin.json, scan subdirectories and suggest available plugins.
Copilot AI review requested due to automatic review settings March 20, 2026 02:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support to kimi plugin install for repositories that contain multiple plugins in subdirectories by parsing git URLs into a clone URL plus an optional subpath, then resolving/validating that subpath after cloning and suggesting available plugins when plugin.json is not at repo root.

Changes:

  • Introduces _parse_git_url() to split git URLs into (clone_url, subpath) and handle common GitHub/GitLab URL patterns.
  • Updates _resolve_source() to clone clone_url, then install from a validated subpath or scan/suggest sub-plugins when the repo root isn’t a plugin.
  • Adds unit + integration-style tests covering URL parsing and subpath resolution behavior (including traversal rejection).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/kimi_cli/cli/plugin.py Adds git URL parsing and extends git clone resolution to support subpaths and plugin discovery/suggestions.
tests/core/test_plugin.py Adds parametrized tests for _parse_git_url() and mocked-clone tests for _resolve_source() subpath behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +44 to +49
# Strip tree/{branch}/ prefix (single-segment branch only)
if len(rest_segments) >= 2 and rest_segments[0] == "tree":
rest_segments = rest_segments[2:]

subpath = "/".join(rest_segments) or None
return clone_url, subpath
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_parse_git_url() strips tree/{branch}/ but drops the branch entirely, and _resolve_source() always clones the default branch. This will install the wrong content for URLs that specify a non-default branch (e.g., /tree/develop/...). Consider parsing and returning the ref/branch and passing it to git clone (e.g., --branch/--single-branch), or explicitly rejecting non-default-branch browser URLs with a clear error.

Copilot uses AI. Check for mistakes.

# Scan one level for available plugins
available = sorted(
d.name for d in repo_root.iterdir() if d.is_dir() and (d / "plugin.json").exists()
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The available = sorted(...) comprehension is on a single line and appears to exceed the repo’s Ruff line-length = 100 limit (E501 is not ignored for src/**). This will likely fail lint/typecheck CI; please wrap the generator and conditions across multiple lines.

Suggested change
d.name for d in repo_root.iterdir() if d.is_dir() and (d / "plugin.json").exists()
d.name
for d in repo_root.iterdir()
if d.is_dir() and (d / "plugin.json").exists()

Copilot uses AI. Check for mistakes.
"my-plugin",
),
(
"https://gitlab.com/org/repo/tree/main/my-plugin",
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These GitLab “browser URL” test cases use /tree/<branch>/..., but GitLab’s canonical web URLs typically include /-/tree/<branch>/.... As written, the tests may give a false sense of GitLab support. Either adjust the parser (and tests) to handle /-/tree/ or constrain the docs/tests to GitHub-style URLs only.

Suggested change
"https://gitlab.com/org/repo/tree/main/my-plugin",
"https://gitlab.com/org/repo/-/tree/main/my-plugin",

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61c36f0f8b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +45 to +46
if len(rest_segments) >= 2 and rest_segments[0] == "tree":
rest_segments = rest_segments[2:]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the selected branch from browser tree/... URLs

When a user pastes a browser URL such as https://github.com/org/repo/tree/release-1.2/my-plugin, this code removes tree/release-1.2 but never carries the branch into _resolve_source(), which still runs plain git clone --depth 1 <repo>. I checked git clone -h: --branch is what makes clone checkout <branch> instead of the remote's HEAD. For any non-default branch, install will either report subpath not found or silently install the copy from the default branch if the same path exists there.

Useful? React with 👍 / 👎.

Comment on lines +40 to +41
owner_repo = "/".join(segments[:2])
clone_url = f"{parsed.scheme}://{parsed.netloc}/{owner_repo}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve GitLab subgroup namespaces in short URLs

GitLab short URLs are not always owner/repo: projects under subgroups have paths like group/subgroup/repo. Slicing to segments[:2] turns https://gitlab.com/group/subgroup/repo/my-plugin into a clone URL of https://gitlab.com/group/subgroup and a subpath of repo/my-plugin, so subgroup-hosted plugin repos cannot be cloned or resolved at all even though this change advertises GitLab short URL support.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional findings.

Open in Devin Review

- _parse_git_url now returns (clone_url, subpath, branch) triple
- Pass --branch to git clone when branch is extracted from tree/ URLs
- Handle GitLab's /-/tree/{branch}/ URL format
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 92014516cd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +51 to +53
if len(rest_segments) >= 2 and rest_segments[0] == "tree":
branch = rest_segments[1]
rest_segments = rest_segments[2:]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle slash-containing tree refs correctly

When a pasted browser URL points at a ref containing / (for example feature/foo or GitHub’s dependabot/... branches), _parse_git_url() only captures rest_segments[1] as the branch and treats the remaining ref segments as part of the plugin path. _resolve_source() then runs git clone --branch <first-segment> and looks for the plugin under the wrong directory, so valid .../tree/<ref>/<plugin> URLs either fail to clone or resolve the wrong source tree.

Useful? React with 👍 / 👎.

@tempurai tempurai merged commit c2ef61b into main Mar 20, 2026
14 checks passed
@tempurai tempurai deleted the feat/multi-plugin-repo branch March 20, 2026 03:56
tempurai added a commit that referenced this pull request Mar 23, 2026
Add comprehensive documentation for the plugin system:

- Add changelog entries for plugin system, multi-plugin repos, and
  credential injection with OAuth/env var support
- Create new plugins.md page (zh/en) covering installation, creation,
  credential injection, and tool script specifications
- Update skills.md to add comparison between Skills and Plugins
- Update vitepress config to include Plugins in navigation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants