oh-my-space 0.9.0 → 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 CHANGED
@@ -1,38 +1,27 @@
1
1
  # oh-my-space
2
2
 
3
- `oh-my-space` provides the `oms` CLI for managing external source repositories declared in `oms.yaml`. Each registered repo is checked out as a **git submodule** at `oms/<alias>/`, so its pinned commit, branch, and history are tracked natively by your project — and `oms` wraps the submodule foot-guns (detached HEAD, remote-first branch creation, easy-to-miss pointer moves) behind a small, friendly command set.
3
+ [![npm version](https://img.shields.io/npm/v/oh-my-space.svg)](https://www.npmjs.com/package/oh-my-space)
4
4
 
5
- Package name: `oh-my-space`
5
+ `oh-my-space` is a small CLI for managing multi-repo workspaces with Git submodules.
6
6
 
7
- Command name: `oms`
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
- ## Why submodules + a wrapper
9
+ ## When to use it
10
10
 
11
- Git submodules give you three things for free: 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). What they make awkward is day-to-day branch work. `oms` keeps the native benefits and removes the friction:
12
-
13
- - **Create a new branch locally, with no remote precondition.** `oms switch <alias> <branch>` makes the branch right away even if it does not exist on the remote yet; `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`.
14
- - **Always on a branch, never detached.** `oms sync` attaches the baseline branch at the pinned commit instead of leaving a detached HEAD.
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` for running `oms`; development uses the version in `.nvmrc` (`24`)
22
- - git `>=2.40` (`git switch` + submodule commands)
23
- - The workspace must be a git repository (`git init`), since sources are submodules of it.
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`, or create one by hand in your project root:
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,53 +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 work:
46
+ Sync the declared repositories and check their state:
62
47
 
63
48
  ```bash
64
- oms init # scaffold a starter oms.yaml
65
- oms doctor # validate oms.yaml + submodule state
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
49
+ oms sync --all # add/initialize every declared repo on its baseline branch
50
+ oms status # branch / pointer / dirty / ahead-behind per submodule
75
51
  ```
76
52
 
77
- For `oms switch` and `oms checkout`, omitting the alias and/or branch prompts you to pick one interactively (synced submodules, and local or `origin/*` branches respectively).
53
+ ## Layout
78
54
 
79
- A typical new-branch flow:
55
+ A workspace with two declared repositories, `api` and `web`, looks like this:
80
56
 
81
- ```bash
82
- oms switch api feature/login # local branch, no remote needed
83
- # ... edit, commit inside oms/api ...
84
- oms push api # creates origin/feature/login and stages the pointer
85
- git add oms/api && git commit # record the pointer in your project history
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)
86
63
  ```
87
64
 
88
- ## Layout
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.
89
66
 
90
- For an alias `api` checked out on `main`:
67
+ ## Typical branch flow
91
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:
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
92
76
  ```
93
- .gitmodules # registers oms/api -> origin url, branch
94
- oms/
95
- └── api/ # git submodule (a normal working tree, on a branch)
96
- ```
97
77
 
98
- `.gitmodules` and the `oms/<alias>` gitlink are part of your project history. Submodules must **not** be gitignored; `oms sync` removes a stale `oms/` entry from `.gitignore` if a previous version added one.
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.
99
106
 
100
- ## Managing source repositories
107
+ ## Command reference
101
108
 
102
109
  `oms.yaml` declares each source repo with `alias`, a `remotes` mapping (which must include `origin`), and optional `branch` (the baseline).
103
110
 
@@ -105,14 +112,40 @@ oms/
105
112
  | --- | --- | --- | --- |
106
113
  | `oms init` | current directory | Writes a starter `oms.yaml`. | Refuses if `oms.yaml` exists; use `--force`. Does not gitignore `oms/`. |
107
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. |
108
- | `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. |
109
- | `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 — stage/commit it. |
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`. |
110
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). |
111
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`. |
112
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`. |
113
- | `oms pull ...` | workspace root | `git pull --ff-only <remote>` on each submodule's current branch, then stages the moved pointer. | Requires the submodule to be on a branch. `--remote <name>` selects a single remote (defaults to `origin`). |
114
- | `oms push <alias>...` | workspace root | `git push <remote> <branch>` (creating the remote branch on first push), then stages the moved pointer. | `--remote <name>` (repeatable) picks the remote(s), defaults to `origin`; upstream is set only for `origin`. `--commit` also commits the pointer update in the parent. |
115
- | `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). |
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. |
127
+
128
+ ## Updating the CLI
129
+
130
+ Check whether the installed CLI is current:
131
+
132
+ ```bash
133
+ oms update --check
134
+ ```
135
+
136
+ Run an update when `oms` can confidently identify a global npm, pnpm, Yarn classic, or Bun installation:
137
+
138
+ ```bash
139
+ oms update
140
+ ```
141
+
142
+ For automation, `--yes` skips the confirmation prompt after `oms` has printed the detected context and selected command:
143
+
144
+ ```bash
145
+ oms update --yes
146
+ ```
147
+
148
+ `oms update` does not edit project manifests or temporary runner caches. If the install is project-local, temporary, development, or unknown, it prints safe manual guidance instead of mutating the environment.
116
149
 
117
150
  ## `oms.yaml` format
118
151
 
@@ -138,12 +171,39 @@ Rules:
138
171
  - Remaining characters: ASCII lowercase letters, digits, `-`, `_`, `@`.
139
172
  - Not allowed: uppercase letters, `/`, `\`, `.`, whitespace.
140
173
  - Pattern: `/^[a-z0-9][a-z0-9_@-]*$/`.
141
- - `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`.
142
- - `branch` is optional; when omitted the remote's default branch is used as the baseline.
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.
143
176
 
144
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).
145
178
 
146
- ## Migrating between versions
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
147
207
 
148
208
  Detailed migration steps are organized per version under [`docs/migrations/`](./docs/migrations/).
149
209
 
@@ -152,6 +212,16 @@ Detailed migration steps are organized per version under [`docs/migrations/`](./
152
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/`
153
213
  - [0.2.x → 0.3.0](./docs/migrations/0.2.x-to-0.3.0.md) — (historical) switched submodules to bare clone + worktrees
154
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
+
155
225
  ## License
156
226
 
157
227
  [MIT](./LICENSE)
@@ -1 +1 @@
1
- {"commit":"b2c2b0bb4e51a35fc36da6d65f40229c896f1d70"}
1
+ {"commit":"0cc84f79dc3659f53fd76a39f9e5ecbe33337651"}