multi-workspace 3.2.1__tar.gz → 3.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/.gitignore +6 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/PKG-INFO +61 -4
- multi_workspace-3.2.2/README.md +94 -0
- multi_workspace-3.2.2/multi/_version.py +24 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/add.py +35 -22
- multi_workspace-3.2.2/multi/api.py +17 -0
- multi_workspace-3.2.2/multi/app_api.py +561 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/bootstrap.py +1 -1
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/cli.py +6 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/cli_helpers.py +26 -10
- multi_workspace-3.2.2/multi/collaborator.py +369 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/doctor.py +159 -67
- multi_workspace-3.2.2/multi/errors.py +10 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/git_helpers.py +19 -5
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/git_set_branch.py +2 -1
- multi_workspace-3.2.2/multi/ignore_files.py +165 -0
- multi_workspace-3.2.2/multi/init.py +328 -0
- multi_workspace-3.2.2/multi/managed_blocks.py +102 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/paths.py +10 -5
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/remove.py +3 -0
- multi_workspace-3.2.2/multi/repo_urls.py +63 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/repos.py +35 -1
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/resources/init_readme.md +1 -1
- multi_workspace-3.2.2/multi/service.py +115 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/settings.py +5 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/sync.py +46 -25
- multi_workspace-3.2.2/multi/sync_agents.py +147 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/sync_github.py +6 -4
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/sync_vscode.py +10 -8
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/sync_vscode_devcontainer.py +4 -3
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/sync_vscode_extensions.py +4 -3
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/sync_vscode_launch.py +6 -5
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/sync_vscode_settings.py +72 -26
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/sync_vscode_tasks.py +4 -3
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/utils.py +60 -37
- multi_workspace-3.2.2/multi/worktree.py +235 -0
- multi_workspace-3.2.1/README.md +0 -37
- multi_workspace-3.2.1/multi/_version.py +0 -34
- multi_workspace-3.2.1/multi/errors.py +0 -22
- multi_workspace-3.2.1/multi/ignore_files.py +0 -201
- multi_workspace-3.2.1/multi/init.py +0 -99
- multi_workspace-3.2.1/multi/rules.py +0 -100
- multi_workspace-3.2.1/multi/sync_ruff.py +0 -85
- multi_workspace-3.2.1/multi/sync_rules.py +0 -141
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/LICENSE +0 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/__init__.py +0 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/__main__.py +0 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/convert_monorepo.py +0 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/git_run.py +0 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/logging.py +0 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/open.py +0 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/registry.py +0 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/multi/sync_vscode_helpers.py +0 -0
- {multi_workspace-3.2.1 → multi_workspace-3.2.2}/pyproject.toml +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multi-workspace
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.2
|
|
4
4
|
Summary: Multi
|
|
5
5
|
Project-URL: Repository, https://github.com/gabemontague/multi
|
|
6
6
|
Project-URL: Issues, https://github.com/gabemontague/multi/issues
|
|
@@ -34,7 +34,8 @@ Description-Content-Type: text/markdown
|
|
|
34
34
|
Features:
|
|
35
35
|
|
|
36
36
|
- Generates files in your root `.vscode` folder from sub-repo `launch.json`, `tasks.json`, and `settings.json` files.
|
|
37
|
-
-
|
|
37
|
+
- Supports optional `installSets` so installer scripts can sync only a public/runtime subset of repos.
|
|
38
|
+
- Optionally generates `CLAUDE.md` and `AGENTS.md` files from tracked `AGENTS.parts/*.md` files.
|
|
38
39
|
- In monorepo mode, syncs sub-repo GitHub workflows into root `.github/workflows`.
|
|
39
40
|
|
|
40
41
|
## Installation
|
|
@@ -57,8 +58,64 @@ To get started, create a new workspace directory that will house all your relate
|
|
|
57
58
|
multi init
|
|
58
59
|
```
|
|
59
60
|
|
|
60
|
-
When prompted, paste in the URLs of all the repositories you want to have in your workspace. You can optionally specify descriptions of what they do, which
|
|
61
|
+
When prompted, paste in the URLs of all the repositories you want to have in your workspace. You can optionally specify descriptions of what they do, which can be included in generated agent instruction files when that feature is enabled.
|
|
61
62
|
|
|
62
|
-
For
|
|
63
|
+
For scripts, you can also initialize non-interactively:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
multi init \
|
|
67
|
+
--repo https://github.com/org/backend \
|
|
68
|
+
--repo-description "Backend API" \
|
|
69
|
+
--repo https://github.com/org/frontend \
|
|
70
|
+
--repo-description "Frontend app"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
To create GitHub repositories first, add `--github-repo` entries. This uses `gh repo create` and defaults to private visibility:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
multi init \
|
|
77
|
+
--github-repo org/backend \
|
|
78
|
+
--github-description "Backend API" \
|
|
79
|
+
--github-repo org/frontend \
|
|
80
|
+
--github-description "Frontend app"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
For `--github-repo`, the GitHub CLI (`gh`) must already be installed and authenticated.
|
|
84
|
+
|
|
85
|
+
When repository slugs are product-prefixed, `multi init` automatically writes short local directory names into `multi.json` when the slug matches the workspace name prefix. For example, in a `t-ide/` workspace, `t-ide-cli` becomes local folder `cli`.
|
|
86
|
+
|
|
87
|
+
The same naming rule applies to `multi add`, so adding `https://github.com/org/t-ide-cli` inside a `t-ide/` workspace will also default to local folder `cli`.
|
|
88
|
+
|
|
89
|
+
For public installer workflows, add `installSets` to repo entries and run a filtered sync:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"repos": [
|
|
94
|
+
{ "name": "cli", "url": "https://github.com/org/product-cli", "installSets": ["default", "dev"] },
|
|
95
|
+
{ "name": "ios", "url": "git@github.com:org/product-ios.git", "installSets": ["dev"] }
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
multi sync --install-set default
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Running `multi sync` without `--install-set` still includes every repo. Repos without `installSets` are included in every named set.
|
|
105
|
+
|
|
106
|
+
To grant or remove GitHub collaborator access across every GitHub repo in a workspace, use:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
multi collaborator add octocat --permission maintain --yes
|
|
110
|
+
multi collaborator add --yes
|
|
111
|
+
multi collaborator remove octocat --yes
|
|
112
|
+
multi collaborator recent-users
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
When the workspace root repository has a GitHub `origin`, `multi collaborator` applies the change there too.
|
|
116
|
+
|
|
117
|
+
Recent collaborator usernames are saved under `~/.multi`, and `multi collaborator add` can prompt from that list when no username is supplied.
|
|
118
|
+
|
|
119
|
+
If one repo fails, `multi collaborator` continues through the rest of the workspace and reports the failures at the end.
|
|
63
120
|
|
|
64
121
|
It is recommended you also install the [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=montaguegabe.multi-workspace) that automatically keeps your project synced when edits are made to synced files. To manually sync, you can run `multi sync`.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# multi
|
|
2
|
+
|
|
3
|
+
`multi` is a better way to work with VS Code/Cursor on multiple Git repos at once. It is an alternative to [multi-root workspaces](https://code.visualstudio.com/docs/editing/workspaces/multi-root-workspaces) that offers more flexibility and control. With `multi`, you can gain control over how tasks, debug runnables, and various IDE and linter settings are combined from multiple project repos ("sub-repos") located within a root workspace folder.
|
|
4
|
+
|
|
5
|
+
**[Documentation](https://multi.bighelp.ai/)**
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
|
|
9
|
+
- Generates files in your root `.vscode` folder from sub-repo `launch.json`, `tasks.json`, and `settings.json` files.
|
|
10
|
+
- Supports optional `installSets` so installer scripts can sync only a public/runtime subset of repos.
|
|
11
|
+
- Optionally generates `CLAUDE.md` and `AGENTS.md` files from tracked `AGENTS.parts/*.md` files.
|
|
12
|
+
- In monorepo mode, syncs sub-repo GitHub workflows into root `.github/workflows`.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### Using `pipx`:
|
|
17
|
+
|
|
18
|
+
- Install [pipx](https://github.com/pypa/pipx)
|
|
19
|
+
- Run `pipx install multi-workspace`
|
|
20
|
+
|
|
21
|
+
### Using `uv`
|
|
22
|
+
|
|
23
|
+
- Install [uv](https://docs.astral.sh/uv/getting-started/installation/)
|
|
24
|
+
- Run `uv tool install multi-workspace`
|
|
25
|
+
|
|
26
|
+
## Getting started
|
|
27
|
+
|
|
28
|
+
To get started, create a new workspace directory that will house all your related repos and run:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
multi init
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
When prompted, paste in the URLs of all the repositories you want to have in your workspace. You can optionally specify descriptions of what they do, which can be included in generated agent instruction files when that feature is enabled.
|
|
35
|
+
|
|
36
|
+
For scripts, you can also initialize non-interactively:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
multi init \
|
|
40
|
+
--repo https://github.com/org/backend \
|
|
41
|
+
--repo-description "Backend API" \
|
|
42
|
+
--repo https://github.com/org/frontend \
|
|
43
|
+
--repo-description "Frontend app"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
To create GitHub repositories first, add `--github-repo` entries. This uses `gh repo create` and defaults to private visibility:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
multi init \
|
|
50
|
+
--github-repo org/backend \
|
|
51
|
+
--github-description "Backend API" \
|
|
52
|
+
--github-repo org/frontend \
|
|
53
|
+
--github-description "Frontend app"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For `--github-repo`, the GitHub CLI (`gh`) must already be installed and authenticated.
|
|
57
|
+
|
|
58
|
+
When repository slugs are product-prefixed, `multi init` automatically writes short local directory names into `multi.json` when the slug matches the workspace name prefix. For example, in a `t-ide/` workspace, `t-ide-cli` becomes local folder `cli`.
|
|
59
|
+
|
|
60
|
+
The same naming rule applies to `multi add`, so adding `https://github.com/org/t-ide-cli` inside a `t-ide/` workspace will also default to local folder `cli`.
|
|
61
|
+
|
|
62
|
+
For public installer workflows, add `installSets` to repo entries and run a filtered sync:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"repos": [
|
|
67
|
+
{ "name": "cli", "url": "https://github.com/org/product-cli", "installSets": ["default", "dev"] },
|
|
68
|
+
{ "name": "ios", "url": "git@github.com:org/product-ios.git", "installSets": ["dev"] }
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
multi sync --install-set default
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Running `multi sync` without `--install-set` still includes every repo. Repos without `installSets` are included in every named set.
|
|
78
|
+
|
|
79
|
+
To grant or remove GitHub collaborator access across every GitHub repo in a workspace, use:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
multi collaborator add octocat --permission maintain --yes
|
|
83
|
+
multi collaborator add --yes
|
|
84
|
+
multi collaborator remove octocat --yes
|
|
85
|
+
multi collaborator recent-users
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
When the workspace root repository has a GitHub `origin`, `multi collaborator` applies the change there too.
|
|
89
|
+
|
|
90
|
+
Recent collaborator usernames are saved under `~/.multi`, and `multi collaborator add` can prompt from that list when no username is supplied.
|
|
91
|
+
|
|
92
|
+
If one repo fails, `multi collaborator` continues through the rest of the workspace and reports the failures at the end.
|
|
93
|
+
|
|
94
|
+
It is recommended you also install the [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=montaguegabe.multi-workspace) that automatically keeps your project synced when edits are made to synced files. To manually sync, you can run `multi sync`.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '3.2.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (3, 2, 2)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -7,24 +7,16 @@ import click
|
|
|
7
7
|
import git
|
|
8
8
|
|
|
9
9
|
from multi.paths import Paths
|
|
10
|
+
from multi.repo_urls import (
|
|
11
|
+
derive_explicit_local_name,
|
|
12
|
+
derive_repo_slug_from_url,
|
|
13
|
+
normalize_repo_url,
|
|
14
|
+
)
|
|
10
15
|
from multi.sync import sync
|
|
11
16
|
|
|
12
17
|
logger = logging.getLogger(__name__)
|
|
13
18
|
|
|
14
19
|
|
|
15
|
-
def _normalize_url(url: str) -> str:
|
|
16
|
-
"""Normalize a repo URL by stripping trailing / and .git for comparison."""
|
|
17
|
-
url = url.rstrip("/")
|
|
18
|
-
if url.endswith(".git"):
|
|
19
|
-
url = url[:-4]
|
|
20
|
-
return url
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _derive_name_from_url(url: str) -> str:
|
|
24
|
-
"""Derive a directory name from a repo URL (last path component)."""
|
|
25
|
-
return url.split("/")[-1]
|
|
26
|
-
|
|
27
|
-
|
|
28
20
|
@click.command(name="add")
|
|
29
21
|
@click.argument("repo_url")
|
|
30
22
|
@click.option("--name", default=None, help="Override directory name derived from URL")
|
|
@@ -46,24 +38,43 @@ def add_cmd(repo_url: str, name: str | None):
|
|
|
46
38
|
repos_list = config.get("repos", [])
|
|
47
39
|
|
|
48
40
|
# Check for duplicate URL
|
|
49
|
-
normalized_new =
|
|
41
|
+
normalized_new = normalize_repo_url(repo_url)
|
|
50
42
|
for entry in repos_list:
|
|
51
43
|
existing_url = entry.get("url", "")
|
|
52
|
-
if
|
|
44
|
+
if normalize_repo_url(existing_url) == normalized_new:
|
|
53
45
|
raise click.ClickException(
|
|
54
46
|
f"A repo with URL '{existing_url}' already exists in multi.json."
|
|
55
47
|
)
|
|
56
48
|
|
|
57
49
|
# Derive or validate name
|
|
58
|
-
|
|
50
|
+
auto_name = derive_explicit_local_name(
|
|
51
|
+
repo_url,
|
|
52
|
+
paths.root_dir.name,
|
|
53
|
+
)
|
|
54
|
+
slug_name = derive_repo_slug_from_url(repo_url)
|
|
55
|
+
|
|
56
|
+
existing_names = {
|
|
57
|
+
entry.get("name") or derive_repo_slug_from_url(entry.get("url", ""))
|
|
58
|
+
for entry in repos_list
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (
|
|
62
|
+
not name
|
|
63
|
+
and auto_name
|
|
64
|
+
and (
|
|
65
|
+
auto_name in existing_names
|
|
66
|
+
or (paths.root_dir / auto_name).exists()
|
|
67
|
+
)
|
|
68
|
+
):
|
|
69
|
+
auto_name = None
|
|
70
|
+
|
|
71
|
+
repo_name = name if name else (auto_name or slug_name)
|
|
59
72
|
|
|
60
73
|
# Check for duplicate name
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
f"A repo with name '{repo_name}' already exists in multi.json."
|
|
66
|
-
)
|
|
74
|
+
if repo_name in existing_names:
|
|
75
|
+
raise click.ClickException(
|
|
76
|
+
f"A repo with name '{repo_name}' already exists in multi.json."
|
|
77
|
+
)
|
|
67
78
|
|
|
68
79
|
# Check target directory doesn't already exist
|
|
69
80
|
target_dir = paths.root_dir / repo_name
|
|
@@ -86,6 +97,8 @@ def add_cmd(repo_url: str, name: str | None):
|
|
|
86
97
|
new_entry: dict = {"url": repo_url}
|
|
87
98
|
if name:
|
|
88
99
|
new_entry["name"] = name
|
|
100
|
+
elif auto_name:
|
|
101
|
+
new_entry["name"] = auto_name
|
|
89
102
|
|
|
90
103
|
repos_list.append(new_entry)
|
|
91
104
|
config["repos"] = repos_list
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from multi.sync import sync
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def sync_workspace(
|
|
7
|
+
root_dir: str | Path,
|
|
8
|
+
*,
|
|
9
|
+
install_set: str | None = None,
|
|
10
|
+
ensure_on_same_branch: bool = True,
|
|
11
|
+
) -> None:
|
|
12
|
+
"""Sync a Multi workspace from Python code."""
|
|
13
|
+
sync(
|
|
14
|
+
root_dir=Path(root_dir),
|
|
15
|
+
ensure_on_same_branch=ensure_on_same_branch,
|
|
16
|
+
install_set=install_set,
|
|
17
|
+
)
|