oh-my-space 0.9.1 → 0.11.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 +108 -64
- package/dist/build-info.json +1 -1
- package/dist/oms.js +990 -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,94 @@ 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
|
-
oms sync --list # list registered source repos
|
|
67
|
-
oms sync <alias>... # add/initialize submodules on their baseline branch
|
|
68
|
-
oms sync --all # sync every registered source repo
|
|
69
|
-
oms status # branch / pointer / dirty / ahead-behind per submodule
|
|
70
|
-
oms switch <alias> <branch> # switch to a local branch, or create a brand-new one
|
|
71
|
-
oms checkout <alias> <branch> # fetch origin, then check out a remote branch (tracking)
|
|
72
|
-
oms push <alias>... # push the branch (lazy remote) + stage the pointer
|
|
73
|
-
oms push <alias> --remote upstream # target a specific remote (repeatable; defaults to origin)
|
|
74
|
-
oms unsync <alias>... # deinitialize and remove a submodule
|
|
75
|
-
oms update --check # check whether a newer oms CLI is available
|
|
49
|
+
oms sync --all # add/initialize every declared repo on its baseline branch
|
|
50
|
+
oms status # branch / pointer / dirty / ahead-behind per submodule
|
|
76
51
|
```
|
|
77
52
|
|
|
78
|
-
|
|
53
|
+
## Layout
|
|
79
54
|
|
|
80
|
-
A
|
|
55
|
+
A workspace with two declared repositories, `api` and `web`, looks like this:
|
|
81
56
|
|
|
82
|
-
```
|
|
83
|
-
oms
|
|
84
|
-
#
|
|
85
|
-
oms
|
|
86
|
-
|
|
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)
|
|
87
63
|
```
|
|
88
64
|
|
|
89
|
-
|
|
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.
|
|
66
|
+
|
|
67
|
+
## Typical branch flow
|
|
90
68
|
|
|
91
|
-
|
|
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:
|
|
92
70
|
|
|
71
|
+
```bash
|
|
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
|
|
93
76
|
```
|
|
94
|
-
|
|
95
|
-
oms
|
|
96
|
-
|
|
77
|
+
|
|
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.
|
|
79
|
+
|
|
80
|
+
Omit the alias or branch on `oms switch` and `oms checkout` to pick one interactively — synced submodules, and local or `origin/*` branches respectively.
|
|
81
|
+
|
|
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:
|
|
85
|
+
|
|
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.
|
|
89
|
+
|
|
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
|
+
## Workspace skills
|
|
108
|
+
|
|
109
|
+
`oms` publishes three installable agent skills that carry the workspace Git guardrail to sessions started anywhere in the workspace — including at the root, which the [`oms agent install`](#ai-agent-workflow) marker under `oms/` cannot reach. They are installed with the external Vercel Labs `skills` tool (`npx skills`):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx skills add divlook/oh-my-space # project scope: install at the workspace root
|
|
113
|
+
npx skills add divlook/oh-my-space -g # global scope: available in every workspace
|
|
114
|
+
npx skills add divlook/oh-my-space --skill oms-pointer # install one skill by name
|
|
115
|
+
npx skills add divlook/oh-my-space --list # list the available skills without installing
|
|
97
116
|
```
|
|
98
117
|
|
|
99
|
-
|
|
118
|
+
Project scope is the default and recommended: these skills are only relevant in an `oms.yaml` workspace, so a project install keeps them out of unrelated repositories. `oms skills` prints these commands, and `oms skills --install` runs the project install for you, resolving to the workspace root first and forwarding extra arguments (`-g`, `--skill`, `--list`, `--copy`) straight through.
|
|
100
119
|
|
|
101
|
-
|
|
120
|
+
The three skills are named by the Git domain each manages:
|
|
121
|
+
|
|
122
|
+
| Skill | Use it when | What it does |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| `oms-workspace` | Scope-ambiguous Git work in the workspace — committing from the root, a moved `oms status` pointer, a push, or adding/removing a repo with `oms sync`/`oms unsync`. | Establishes workspace state and root-versus-submodule scope before acting, and separates repo add/remove topology from recording a moved pointer. |
|
|
125
|
+
| `oms-pointer` | After `oms commit` or `oms pull` moves a submodule's commit. | Records the moved root pointer with `oms record`, so a submodule change is not left without a recorded pointer and the root pointer is not committed by mistake. |
|
|
126
|
+
| `oms-branch` | Starting or switching a branch inside a submodule. | Chooses `oms switch` (new local branch) versus `oms checkout` (track a remote branch) and avoids detached HEAD. |
|
|
127
|
+
|
|
128
|
+
Skill firing is best-effort — an agent loads a skill only when it judges the skill's description relevant — so the skills complement, rather than replace, `oms <command> --help` and the always-on marker block inside `oms/`. Each skill defers exact `oms status --json` field semantics to `oms status --help`, the version-matched authoritative source that ships with the installed CLI.
|
|
129
|
+
|
|
130
|
+
## Command reference
|
|
102
131
|
|
|
103
132
|
`oms.yaml` declares each source repo with `alias`, a `remotes` mapping (which must include `origin`), and optional `branch` (the baseline).
|
|
104
133
|
|
|
@@ -106,14 +135,18 @@ oms/
|
|
|
106
135
|
| --- | --- | --- | --- |
|
|
107
136
|
| `oms init` | current directory | Writes a starter `oms.yaml`. | Refuses if `oms.yaml` exists; use `--force`. Does not gitignore `oms/`. |
|
|
108
137
|
| `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 —
|
|
138
|
+
| `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 ...`). |
|
|
139
|
+
| `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. |
|
|
140
|
+
| `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. |
|
|
141
|
+
| `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
142
|
| `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
143
|
| `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
144
|
| `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. |
|
|
145
|
+
| `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`). |
|
|
146
|
+
| `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`. |
|
|
147
|
+
| `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 ...`). |
|
|
148
|
+
| `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). |
|
|
149
|
+
| `oms skills` | anywhere (`--install` resolves to root) | Prints the `npx skills add divlook/oh-my-space` commands (project scope and `-g` global) to install the workspace skills. | `--install` delegates to `npx skills add`, forwarding extra args (`-g`, `--skill <name>`, `--list`). Run outside a workspace without `-g`, it errors and points to the global install. See [Workspace skills](#workspace-skills). |
|
|
117
150
|
| `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
151
|
|
|
119
152
|
## Updating the CLI
|
|
@@ -162,20 +195,31 @@ Rules:
|
|
|
162
195
|
- Remaining characters: ASCII lowercase letters, digits, `-`, `_`, `@`.
|
|
163
196
|
- Not allowed: uppercase letters, `/`, `\`, `.`, whitespace.
|
|
164
197
|
- Pattern: `/^[a-z0-9][a-z0-9_@-]*$/`.
|
|
165
|
-
- `remotes` is required and must include an `origin` entry
|
|
166
|
-
- `branch` is optional
|
|
198
|
+
- `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`.
|
|
199
|
+
- `branch` is optional. When omitted, the remote's default branch is used as the baseline.
|
|
167
200
|
|
|
168
201
|
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
202
|
|
|
170
|
-
##
|
|
203
|
+
## Migration guides
|
|
171
204
|
|
|
172
205
|
Detailed migration steps are organized per version under [`docs/migrations/`](./docs/migrations/).
|
|
173
206
|
|
|
207
|
+
- [0.9.x → 0.10.0](./docs/migrations/0.9.x-to-0.10.0.md) — scopes each command to a single Git boundary and makes root pointer commits explicit via `oms record`
|
|
174
208
|
- [0.7.x → 0.8.0](./docs/migrations/0.7.x-to-0.8.0.md) — splits `oms checkout` into `oms switch` (local branches) and `oms checkout` (remote branches)
|
|
175
209
|
- [0.5.x → 0.6.0](./docs/migrations/0.5.x-to-0.6.0.md) — switches the data model from bare clone + worktrees back to git submodules
|
|
176
210
|
- [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
211
|
- [0.2.x → 0.3.0](./docs/migrations/0.2.x-to-0.3.0.md) — (historical) switched submodules to bare clone + worktrees
|
|
178
212
|
|
|
213
|
+
## Local development
|
|
214
|
+
|
|
215
|
+
This repository targets the Node.js version in [`.nvmrc`](./.nvmrc) (`24`). After cloning:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
nvm use
|
|
219
|
+
npm ci
|
|
220
|
+
npm test
|
|
221
|
+
```
|
|
222
|
+
|
|
179
223
|
## License
|
|
180
224
|
|
|
181
225
|
[MIT](./LICENSE)
|
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"commit":"
|
|
1
|
+
{"commit":"c69910168d4bea81b7b02079ee868a5d99b3cf2d"}
|