oh-my-space 0.10.0 → 0.11.1

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
@@ -43,6 +43,8 @@ repos:
43
43
  branch: main # optional; defaults to the remote's default branch
44
44
  ```
45
45
 
46
+ After scaffolding, `oms init` prints optional AI-setup hints pointing to [`oms agent install`](#ai-agent-workflow) and [`oms skills`](#workspace-skills); both are opt-in and install nothing on their own.
47
+
46
48
  Sync the declared repositories and check their state:
47
49
 
48
50
  ```bash
@@ -104,6 +106,29 @@ When an AI coding agent works in a workspace, the main risk is operating in the
104
106
 
105
107
  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
108
 
109
+ ## Workspace skills
110
+
111
+ `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`):
112
+
113
+ ```bash
114
+ npx skills add divlook/oh-my-space # project scope: install at the workspace root
115
+ npx skills add divlook/oh-my-space -g # global scope: available in every workspace
116
+ npx skills add divlook/oh-my-space --skill oms-pointer # install one skill by name
117
+ npx skills add divlook/oh-my-space --list # list the available skills without installing
118
+ ```
119
+
120
+ 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.
121
+
122
+ The three skills are named by the Git domain each manages:
123
+
124
+ | Skill | Use it when | What it does |
125
+ | --- | --- | --- |
126
+ | `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. |
127
+ | `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. |
128
+ | `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. |
129
+
130
+ 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.
131
+
107
132
  ## Command reference
108
133
 
109
134
  `oms.yaml` declares each source repo with `alias`, a `remotes` mapping (which must include `origin`), and optional `branch` (the baseline).
@@ -123,6 +148,7 @@ When an AI coding agent works in a workspace, the main risk is operating in the
123
148
  | `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
149
  | `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
150
  | `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). |
151
+ | `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). |
126
152
  | `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
153
 
128
154
  ## Updating the CLI
@@ -176,37 +202,11 @@ Rules:
176
202
 
177
203
  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).
178
204
 
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
205
  ## Migration guides
207
206
 
208
207
  Detailed migration steps are organized per version under [`docs/migrations/`](./docs/migrations/).
209
208
 
209
+ - [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`
210
210
  - [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)
211
211
  - [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
212
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/`
@@ -1 +1 @@
1
- {"commit":"0cc84f79dc3659f53fd76a39f9e5ecbe33337651"}
1
+ {"commit":"b7828e49a17a0e77babec31f989a37b19ea57fe3"}
package/dist/oms.js CHANGED
@@ -2014,14 +2014,16 @@ async function finalizeTopology(repoRoot, requested, kind, commit, allSucceeded)
2014
2014
  }
2015
2015
  const OMS_MARKER_START = "<!-- OMS START -->";
2016
2016
  const OMS_MARKER_END = "<!-- OMS END -->";
2017
- /** Concise, durable agent rules; detailed usage is deferred to CLI help. */
2017
+ /** Canonical scope-guardrail kernel, single-sourced into the marker block and each published SKILL.md. */
2018
+ const OMS_SCOPE_GUARDRAIL = `- Run \`oms status --json\` before Git work involving \`oms/\` to read root versus submodule state.
2019
+ - Treat each \`oms/<alias>/\` directory as a separate Git repository.
2020
+ - Use \`oms\` commands for scoped submodule workflows; do not guess root repository versus submodule Git scope.
2021
+ - Do not create root commits for existing submodule pointer updates unless the user explicitly runs \`oms record <alias>\`.`;
2022
+ /** Concise, durable agent rules; detailed usage is deferred to CLI help. The marker's own --help line stays outside the kernel constant. */
2018
2023
  const OMS_INSTRUCTION_BLOCK = `${OMS_MARKER_START}
2019
2024
  ## OMS Workspace Rules
2020
2025
 
2021
- - Run \`oms status --json\` before Git work involving \`oms/\` to read root versus submodule state.
2022
- - Treat each \`oms/<alias>/\` directory as a separate Git repository.
2023
- - Use \`oms\` commands for scoped submodule workflows; do not guess root repository versus submodule Git scope.
2024
- - Do not create root commits for existing submodule pointer updates unless the user explicitly runs \`oms record <alias>\`.
2026
+ ${OMS_SCOPE_GUARDRAIL}
2025
2027
  - Check \`oms --help\` and \`oms <command> --help\` for exact command usage.
2026
2028
  ${OMS_MARKER_END}`;
2027
2029
  function countOccurrences(haystack, needle) {
@@ -2171,6 +2173,45 @@ async function runAgentUninstall(options) {
2171
2173
  }
2172
2174
  return 0;
2173
2175
  }
2176
+ /** npx skills package identifier for the oms workspace skills. */
2177
+ const SKILLS_REPO = "divlook/oh-my-space";
2178
+ /** Extra args forwarded to "npx skills add", read from argv so flags pass through verbatim. */
2179
+ function skillsForwardedArgs() {
2180
+ // process.argv: [node, oms.js, "skills", ...rest]; drop the "--install" flag and forward the rest.
2181
+ return process.argv.slice(3).filter((arg) => arg !== "--install");
2182
+ }
2183
+ /** Print the install commands, or with --install delegate to "npx skills add" from the workspace root. */
2184
+ async function runSkills(install, extraArgs) {
2185
+ const projectCommand = `npx skills add ${SKILLS_REPO}`;
2186
+ const globalCommand = `${projectCommand} -g`;
2187
+ if (!install) {
2188
+ log.info("Install the oms workspace skills with the skills tool:");
2189
+ log.message(` ${projectCommand} # project scope (run at the workspace root)`);
2190
+ log.message(` ${globalCommand} # global scope (every workspace)`);
2191
+ log.message("Add --skill <name> to install one (oms-workspace, oms-pointer, oms-branch), or --list to list them.");
2192
+ return 0;
2193
+ }
2194
+ const wantsGlobal = extraArgs.includes("-g") || extraArgs.includes("--global");
2195
+ const workspaceRoot = findWorkspaceRoot();
2196
+ if (!workspaceRoot && !wantsGlobal) {
2197
+ log.error(`"oms skills --install" must run inside an ${MANIFEST_FILENAME} workspace. ` +
2198
+ `For a global install from anywhere, run: ${globalCommand}`);
2199
+ return 1;
2200
+ }
2201
+ const args = ["skills", "add", SKILLS_REPO, ...extraArgs];
2202
+ const npxBin = testEnv("OMS_NPX_BIN") ?? "npx";
2203
+ const result = spawnSync(npxBin, args, {
2204
+ stdio: "inherit",
2205
+ cwd: workspaceRoot ?? process.cwd(),
2206
+ shell: runtimePlatform() === "win32",
2207
+ });
2208
+ if (result.error || result.status === null) {
2209
+ log.error(`Could not run "${[npxBin, ...args].join(" ")}".`);
2210
+ log.message(`Install the skills manually: ${projectCommand}`);
2211
+ return 1;
2212
+ }
2213
+ return result.status;
2214
+ }
2174
2215
  const INIT_TEMPLATE = `# yaml-language-server: $schema=https://raw.githubusercontent.com/divlook/oh-my-space/main/oms.schema.json
2175
2216
  repos:
2176
2217
  - alias: example
@@ -2194,6 +2235,10 @@ async function runInit(options) {
2194
2235
  log.info(`oms manages sources as git submodules; run "git init" here if this is not a git repo yet.`);
2195
2236
  }
2196
2237
  log.info(`edit alias/remotes/branch, then run "oms sync".`);
2238
+ // Signpost the optional AI-setup commands; output-only, installs nothing.
2239
+ log.info("Optional — set up AI agent guidance for this workspace:");
2240
+ log.message(` oms agent install # add the OMS instruction block for AI agents`);
2241
+ log.message(` oms skills # show how to install the oms workspace skills`);
2197
2242
  return 0;
2198
2243
  }
2199
2244
  async function runDoctor() {
@@ -2420,7 +2465,12 @@ async function exitWith(action) {
2420
2465
  const exitHelp = "\nExit codes: 0 ok | 1 usage/config error | 2 one or more git operations failed.";
2421
2466
  // Per-command help: each new or changed command states its purpose, scope boundary, and an example.
2422
2467
  const statusHelp = `
2423
- Machine-readable mode prints exactly one JSON object on stdout (schemaVersion, root, repos, pointers).
2468
+ Machine-readable mode prints exactly one JSON object on stdout. The schemaVersion 1 payload has seven
2469
+ top-level keys: schemaVersion, toolVersion, workspaceRoot, currentAlias, root, repos, and errors.
2470
+ Submodule pointer movement lives under root.submodulePointers, with moved, staged, split, and conflict
2471
+ arrays (not a top-level "pointers" key). Each repos[] entry summarizes one oms/<alias>/ submodule
2472
+ (alias, path, branch, head, pin, dirty, ahead/behind, error). Read the live --json output for exact
2473
+ per-field values.
2424
2474
  Examples:
2425
2475
  $ oms status --json # full workspace state for tools and agents
2426
2476
  $ oms status api --json # narrow the JSON to one alias
@@ -2477,6 +2527,18 @@ Removes the marker-delimited OMS block; a file left empty is deleted. Missing fi
2477
2527
  Example:
2478
2528
  $ oms agent uninstall --target both
2479
2529
  `;
2530
+ const skillsHelp = `
2531
+ Installs the oms workspace skills (oms-workspace, oms-pointer, oms-branch) via the external "skills" tool.
2532
+ Project scope (installed at the workspace root, discovered from the root and its subdirectories) is the
2533
+ default; -g installs globally for every workspace. With --install, oms resolves to the workspace root and
2534
+ delegates to "npx skills add", forwarding extra arguments (-g, --skill <name>, --list, --copy) straight
2535
+ through to the skills tool.
2536
+ Examples:
2537
+ $ oms skills # print the project and global install commands
2538
+ $ oms skills --install # install the skills project-scoped at the workspace root
2539
+ $ oms skills --install -g # install the skills globally
2540
+ $ oms skills --install --skill oms-branch # install one skill
2541
+ `;
2480
2542
  const commandNames = new Set([
2481
2543
  "init",
2482
2544
  "doctor",
@@ -2491,6 +2553,7 @@ const commandNames = new Set([
2491
2553
  "push",
2492
2554
  "unsync",
2493
2555
  "agent",
2556
+ "skills",
2494
2557
  "update",
2495
2558
  "help",
2496
2559
  ]);
@@ -2636,6 +2699,16 @@ agentCommand
2636
2699
  .action(async (options) => {
2637
2700
  await exitWith(runAgentUninstall(options));
2638
2701
  });
2702
+ program
2703
+ .command("skills")
2704
+ .description("Print the command to install the oms workspace skills, or run it with --install.")
2705
+ .option("--install", "delegate to \"npx skills add\" (forwards extra args such as -g, --skill, --list)")
2706
+ .allowUnknownOption()
2707
+ .argument("[args...]", "extra arguments forwarded to \"npx skills add\" with --install")
2708
+ .addHelpText("after", `${skillsHelp}${exitHelp}`)
2709
+ .action(async (_args, options) => {
2710
+ await exitWith(runSkills(Boolean(options.install), skillsForwardedArgs()));
2711
+ });
2639
2712
  program
2640
2713
  .command("update")
2641
2714
  .description("Check for and safely update the oms CLI. Only confident global installs are updated automatically.")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-space",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "Manage multi-repo workspaces with Git submodules.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/divlook/oh-my-space",