oh-my-space 0.9.1 → 0.10.0
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.
- package/README.md +111 -65
- package/dist/build-info.json +1 -1
- package/dist/oms.js +921 -43
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,38 +1,27 @@
|
|
|
1
1
|
# oh-my-space
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/oh-my-space)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`oh-my-space` is a small CLI for managing multi-repo workspaces with Git submodules.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Declare external repositories in `oms.yaml` and sync them into `oms/<alias>/`. Your parent project records each repo's exact commit while you work with normal branch, pull, and push flows.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## When to use it
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- **Pointer moves stay visible.** `oms pull` / `oms push` stage the updated gitlink in the parent so you can see and commit it; `oms status` surfaces drift.
|
|
16
|
-
|
|
17
|
-
> Sharing a pin with your team still requires pushing the commit — that is inherent to any pinning model. `oms` removes the *local* friction; it cannot make an unpushed commit reproducible for others.
|
|
11
|
+
- You work across several repositories from one project workspace and want them checked out side by side.
|
|
12
|
+
- You want each source repo pinned to an exact commit so your workspace stays reproducible.
|
|
13
|
+
- You want to stay on a real branch during everyday submodule work instead of landing in a detached HEAD.
|
|
14
|
+
- You want pointer changes to show up in `git status` so you can review them before committing.
|
|
18
15
|
|
|
19
16
|
## Requirements
|
|
20
17
|
|
|
21
|
-
- [Node.js](https://nodejs.org) `>=20.19.0`
|
|
22
|
-
- git `>=2.40`
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
For local development:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
nvm use
|
|
29
|
-
npm ci
|
|
30
|
-
npm test
|
|
31
|
-
```
|
|
18
|
+
- [Node.js](https://nodejs.org) `>=20.19.0` to run `oms`.
|
|
19
|
+
- git `>=2.40` for `git switch` and the submodule commands `oms` relies on.
|
|
20
|
+
- Run `oms` from a Git repository. For a new workspace, run `git init` first, since sources are tracked as submodules of it.
|
|
32
21
|
|
|
33
22
|
## Install
|
|
34
23
|
|
|
35
|
-
Install globally with your package manager of choice:
|
|
24
|
+
Install `oh-my-space` to use the `oms` command. Install it globally with your package manager of choice:
|
|
36
25
|
|
|
37
26
|
```bash
|
|
38
27
|
npm install -g oh-my-space
|
|
@@ -43,7 +32,7 @@ bun install -g oh-my-space
|
|
|
43
32
|
|
|
44
33
|
## Quick start
|
|
45
34
|
|
|
46
|
-
Run `oms init` to scaffold a starter `oms.yaml
|
|
35
|
+
Run `oms init` to scaffold a starter `oms.yaml` in your project root, then edit it down to the repositories you need. A minimal one-repo config looks like this:
|
|
47
36
|
|
|
48
37
|
```yaml
|
|
49
38
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/divlook/oh-my-space/main/oms.schema.json
|
|
@@ -51,54 +40,71 @@ repos:
|
|
|
51
40
|
- alias: api
|
|
52
41
|
remotes:
|
|
53
42
|
origin: git@github.com:example/api.git
|
|
54
|
-
branch: main
|
|
55
|
-
- alias: web
|
|
56
|
-
remotes:
|
|
57
|
-
origin: git@github.com:example/web.git
|
|
58
|
-
upstream: git@github.com:upstream/web.git
|
|
43
|
+
branch: main # optional; defaults to the remote's default branch
|
|
59
44
|
```
|
|
60
45
|
|
|
61
|
-
Sync and
|
|
46
|
+
Sync the declared repositories and check their state:
|
|
62
47
|
|
|
63
48
|
```bash
|
|
64
|
-
oms
|
|
65
|
-
oms
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
oms
|
|
74
|
-
|
|
75
|
-
oms
|
|
49
|
+
oms sync --all # add/initialize every declared repo on its baseline branch
|
|
50
|
+
oms status # branch / pointer / dirty / ahead-behind per submodule
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Layout
|
|
54
|
+
|
|
55
|
+
A workspace with two declared repositories, `api` and `web`, looks like this:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
oms.yaml # declares each source repo (alias, remotes, branch)
|
|
59
|
+
.gitmodules # registers each oms/<alias> -> origin url, branch
|
|
60
|
+
oms/
|
|
61
|
+
├── api/ # git submodule (a normal working tree, on a branch)
|
|
62
|
+
└── web/ # git submodule (a normal working tree, on a branch)
|
|
76
63
|
```
|
|
77
64
|
|
|
78
|
-
|
|
65
|
+
`oms.yaml` is your declaration, `.gitmodules` and each `oms/<alias>` gitlink are tracked in your project history, and every directory under `oms/` is a normal checked-out repository you can branch, edit, and commit in.
|
|
79
66
|
|
|
80
|
-
|
|
67
|
+
## Typical branch flow
|
|
68
|
+
|
|
69
|
+
Start a branch, commit inside the submodule, push it, then record the pointer move in your project history. Each command stays in a single Git scope:
|
|
81
70
|
|
|
82
71
|
```bash
|
|
83
|
-
oms switch api feature/login
|
|
84
|
-
|
|
85
|
-
oms push api
|
|
86
|
-
|
|
72
|
+
oms switch api feature/login # local branch, no remote needed
|
|
73
|
+
oms commit api -m "feat: add login" # commit inside oms/api (submodule only)
|
|
74
|
+
oms push api # creates origin/feature/login (submodule branch only)
|
|
75
|
+
oms record api # commit the moved oms/api pointer in your project history
|
|
87
76
|
```
|
|
88
77
|
|
|
89
|
-
|
|
78
|
+
`oms commit` and `oms push` stay inside the submodule and never touch the root gitlink; `oms record` is the only command that commits an existing root pointer update. After a successful `oms commit`/`oms pull`/`oms push`, `oms` prints an `oms record <alias>` hint when the pointer has moved. Run `oms status --json` for a machine-readable view of which scope changed.
|
|
90
79
|
|
|
91
|
-
|
|
80
|
+
Omit the alias or branch on `oms switch` and `oms checkout` to pick one interactively — synced submodules, and local or `origin/*` branches respectively.
|
|
92
81
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
oms
|
|
96
|
-
└── api/ # git submodule (a normal working tree, on a branch)
|
|
97
|
-
```
|
|
82
|
+
## How `oms` uses Git submodules
|
|
83
|
+
|
|
84
|
+
`oms` does not replace Git submodules. It adds a small command layer for the workflow details that make submodules awkward. Submodules already give you a reproducible pin (the parent records each source's exact commit), visibility (`git status` shows when a pointer moved), and history (the pointer travels with your commits). The friction is in everyday branch work, and that is what `oms` smooths over:
|
|
98
85
|
|
|
99
|
-
|
|
86
|
+
- **Start branches locally.** `oms switch <alias> <branch>` starts a local branch right away, even before it exists on the remote. `oms checkout <alias> <branch>` fetches origin and checks out an existing remote branch as a tracking branch. The remote branch is created lazily on your first `oms push`.
|
|
87
|
+
- **Stay on a branch.** `oms sync` attaches the baseline branch at the pinned commit instead of leaving a detached HEAD, so everyday submodule work never strands you off a branch.
|
|
88
|
+
- **Keep pointer moves visible, commit them explicitly.** `oms pull` and `oms push` synchronize only the submodule branch — they never stage or commit the root gitlink. The moved pointer shows up in `git status`, `oms status` flags when a submodule has drifted from the recorded pointer, and `oms record <alias>` commits that pointer update in the parent repo.
|
|
100
89
|
|
|
101
|
-
|
|
90
|
+
Submodules must **not** be gitignored, since the `oms/<alias>` gitlink is what records each pinned commit. `oms sync` removes a stale `oms/` entry from `.gitignore` if a previous version added one.
|
|
91
|
+
|
|
92
|
+
> `oms` makes local submodule work easier, but reproducible sharing still requires pushing the source commit (`oms push`) and recording the parent pointer (`oms record`).
|
|
93
|
+
|
|
94
|
+
## AI agent workflow
|
|
95
|
+
|
|
96
|
+
When an AI coding agent works in a workspace, the main risk is operating in the wrong Git scope — the root repository versus an `oms/<alias>/` submodule. Two features make that boundary explicit:
|
|
97
|
+
|
|
98
|
+
- **`oms status --json`** prints exactly one machine-readable JSON object on stdout (schema-versioned) describing the workspace root, the current alias, root submodule pointers, and each submodule's branch, dirtiness, and ahead/behind state. An agent can inspect it before deciding where to branch or commit.
|
|
99
|
+
- **`oms agent install`** writes a concise, marker-delimited instruction block into `oms/AGENTS.md` and/or `oms/CLAUDE.md`:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
oms agent install --target both # or: --target agents | --target claude
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
These are **root-repository files under `oms/`, not submodule files**. The managed block is delimited by `<!-- OMS START -->` / `<!-- OMS END -->`; content outside the markers is preserved, and `oms agent uninstall` removes only that block (deleting the file if it becomes empty). The files are created but never staged, so you review and commit them yourself.
|
|
106
|
+
|
|
107
|
+
## Command reference
|
|
102
108
|
|
|
103
109
|
`oms.yaml` declares each source repo with `alias`, a `remotes` mapping (which must include `origin`), and optional `branch` (the baseline).
|
|
104
110
|
|
|
@@ -106,14 +112,17 @@ oms/
|
|
|
106
112
|
| --- | --- | --- | --- |
|
|
107
113
|
| `oms init` | current directory | Writes a starter `oms.yaml`. | Refuses if `oms.yaml` exists; use `--force`. Does not gitignore `oms/`. |
|
|
108
114
|
| `oms doctor` | project root or child path | Checks `oms.yaml`, git availability, that the workspace is a git repo, and each alias's submodule state. | Returns exit 2 if any warning is raised. |
|
|
109
|
-
| `oms sync <alias>` / `--all` | workspace root | Registers missing repos with `git submodule add`, initializes registered-but-uninitialized ones, fetches, and attaches the baseline branch. | Reproduces the recorded pointer on a fresh clone. |
|
|
110
|
-
| `oms status [alias...]` / `--all` | anywhere under root | Prints branch, pointer state (`ok`/`moved`/`uninit`), dirtiness, and ahead/behind for each submodule. | `moved` means the working commit differs from the recorded pointer —
|
|
115
|
+
| `oms sync <alias>` / `--all` | workspace root | Registers missing repos with `git submodule add`, initializes registered-but-uninitialized ones, fetches, and attaches the baseline branch. | Reproduces the recorded pointer on a fresh clone. Topology changes (`.gitmodules`, `oms/<alias>`) are left unstaged by default; commit them via the prompt or `--commit` (`chore(oms): add ...`). |
|
|
116
|
+
| `oms status [alias...]` / `--all` | anywhere under root | Prints branch, pointer state (`ok`/`moved`/`uninit`/`missing`/`conflict`), dirtiness, and ahead/behind for each submodule. | `moved` means the working commit differs from the recorded pointer — record it with `oms record`. `--json` prints one machine-readable object on stdout for tooling and agents. |
|
|
117
|
+
| `oms commit [alias]` | workspace root or inside `oms/<alias>/` | Commits source changes inside the selected submodule only; never the root gitlink. | `-m <message>` is required (repeatable). Commits existing staged changes as-is, otherwise stages all with `git add -A`. Infers the alias from the current `oms/<alias>/` directory. |
|
|
118
|
+
| `oms record [alias]` | workspace root or inside `oms/<alias>/` | Commits an existing root gitlink pointer update for one alias (`chore(oms): update <alias> submodule to <sha>`). | Root repo only, path-limited to `oms/<alias>`; refuses unrelated staged changes. Not for adds/removals — use `oms sync`/`oms unsync`. |
|
|
111
119
|
| `oms switch [alias] [branch]` | workspace root | `git switch` to a LOCAL branch, creating it locally if it does not exist yet (no remote required). | `--from <ref>` sets the start point for a new branch. Omit alias/branch to pick interactively (or create a new branch). |
|
|
112
120
|
| `oms checkout [alias] [branch]` | workspace root | `git fetch origin --prune`, then check out a REMOTE branch (`origin/*`) as a local tracking branch (or switch to an existing local counterpart). | Omit alias/branch to pick interactively. To create a brand-new local branch, use `oms switch`. |
|
|
113
121
|
| `oms fetch ...` | workspace root | `git fetch <remote> --prune` in each submodule. | `--remote <name>` (repeatable) picks the remote(s); omit to choose interactively, defaults to `origin`. |
|
|
114
|
-
| `oms pull ...` | workspace root | `git pull --ff-only <remote>` on each submodule's current branch
|
|
115
|
-
| `oms push <alias>...` | workspace root | `git push <remote> <branch>` (creating the remote branch on first push)
|
|
116
|
-
| `oms unsync <alias>` / `--all` | workspace root | `git submodule deinit` + `git rm` for the alias; drops an empty `.gitmodules`. | Keeps the `oms.yaml` entry. Use `--force` to discard uncommitted changes. |
|
|
122
|
+
| `oms pull ...` | workspace root | `git pull --ff-only <remote>` on each submodule's current branch. | Submodule branch only — never stages or commits the root gitlink. Rejects a dirty submodule; prints an `oms record <alias>` hint when the pointer moves. `--remote <name>` selects a single remote (defaults to `origin`). |
|
|
123
|
+
| `oms push <alias>...` | workspace root | `git push <remote> <branch>` (creating the remote branch on first push). | Submodule branch only — never stages or commits the root gitlink. `--commit`/`--record` are unsupported; record the root pointer with `oms record <alias>`. `--remote <name>` (repeatable) picks the remote(s); upstream is set only for `origin`. |
|
|
124
|
+
| `oms unsync <alias>` / `--all` | workspace root | `git submodule deinit` + `git rm` for the alias; drops an empty `.gitmodules`. | Keeps the `oms.yaml` entry. Use `--force` to discard uncommitted changes. Removal topology is left unstaged by default; commit via the prompt or `--commit` (`chore(oms): remove ...`). |
|
|
125
|
+
| `oms agent install` / `uninstall` | workspace root | Manages a marker-delimited OMS instruction block in `oms/AGENTS.md` and/or `oms/CLAUDE.md` (root-repo files). | `--target agents\|claude\|both` (omit to choose interactively). Does not stage files. See [AI agent workflow](#ai-agent-workflow). |
|
|
117
126
|
| `oms update` | anywhere | Checks the npm registry and safely updates the installed `oms` CLI only when it detects a confident global install. | Use `--check` for a non-mutating check. Use `--yes` to skip the confirmation prompt for confident global updates. Project-local, temporary runner, development, and unknown installs print guidance only. |
|
|
118
127
|
|
|
119
128
|
## Updating the CLI
|
|
@@ -162,12 +171,39 @@ Rules:
|
|
|
162
171
|
- Remaining characters: ASCII lowercase letters, digits, `-`, `_`, `@`.
|
|
163
172
|
- Not allowed: uppercase letters, `/`, `\`, `.`, whitespace.
|
|
164
173
|
- Pattern: `/^[a-z0-9][a-z0-9_@-]*$/`.
|
|
165
|
-
- `remotes` is required and must include an `origin` entry
|
|
166
|
-
- `branch` is optional
|
|
174
|
+
- `remotes` is required and must include an `origin` entry. Each value is a clonable git URL. `origin` becomes the submodule's primary remote, and additional remotes are configured on `oms sync`.
|
|
175
|
+
- `branch` is optional. When omitted, the remote's default branch is used as the baseline.
|
|
167
176
|
|
|
168
177
|
JSON schema: [`oms.schema.json`](./oms.schema.json) (also reachable at `https://raw.githubusercontent.com/divlook/oh-my-space/main/oms.schema.json` for YAML LSPs).
|
|
169
178
|
|
|
170
|
-
##
|
|
179
|
+
## Behavior changes
|
|
180
|
+
|
|
181
|
+
The AI-submodule-workflow update scopes each command to a single Git boundary and makes root pointer commits explicit. If you have existing automation, note these changes:
|
|
182
|
+
|
|
183
|
+
- **`oms push --commit` is removed** (and `oms push --record` is unsupported). It fails before pushing. Push the submodule branch, then record the root pointer separately:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# before
|
|
187
|
+
oms push api --commit
|
|
188
|
+
|
|
189
|
+
# after
|
|
190
|
+
oms push api # pushes the submodule branch only
|
|
191
|
+
oms record api # commits the moved oms/api pointer in the parent
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
- **`oms pull` and `oms push` no longer stage the root gitlink.** They synchronize only the submodule branch; the moved pointer shows up in `git status` and is committed with `oms record <alias>`.
|
|
195
|
+
|
|
196
|
+
- **`oms sync` and `oms unsync` no longer leave the root index staged.** Topology changes (`.gitmodules` and `oms/<alias>`) stay in the working tree, unstaged. Create the topology commit through the interactive prompt or with `--commit`:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# before: sync left .gitmodules + oms/api staged for you to commit
|
|
200
|
+
oms sync api && git commit -m "add api"
|
|
201
|
+
|
|
202
|
+
# after: topology is unstaged by default
|
|
203
|
+
oms sync api --commit # creates chore(oms): add api submodule
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Migration guides
|
|
171
207
|
|
|
172
208
|
Detailed migration steps are organized per version under [`docs/migrations/`](./docs/migrations/).
|
|
173
209
|
|
|
@@ -176,6 +212,16 @@ Detailed migration steps are organized per version under [`docs/migrations/`](./
|
|
|
176
212
|
- [0.3.x → 0.4.0](./docs/migrations/0.3.x-to-0.4.0.md) — renames `sources.yaml`/`sources/` to `oms.yaml`/`oms/`
|
|
177
213
|
- [0.2.x → 0.3.0](./docs/migrations/0.2.x-to-0.3.0.md) — (historical) switched submodules to bare clone + worktrees
|
|
178
214
|
|
|
215
|
+
## Local development
|
|
216
|
+
|
|
217
|
+
This repository targets the Node.js version in [`.nvmrc`](./.nvmrc) (`24`). After cloning:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
nvm use
|
|
221
|
+
npm ci
|
|
222
|
+
npm test
|
|
223
|
+
```
|
|
224
|
+
|
|
179
225
|
## License
|
|
180
226
|
|
|
181
227
|
[MIT](./LICENSE)
|
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"commit":"
|
|
1
|
+
{"commit":"0cc84f79dc3659f53fd76a39f9e5ecbe33337651"}
|