@esneiderbravo/speclaw 2.0.0 → 2.0.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
@@ -332,14 +332,19 @@ review from Code Owners* when you declare `team.owners`.
332
332
  ## <img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/diamond.png" height="20" alt="◆" align="absmiddle">&nbsp; Staying up to date
333
333
 
334
334
  speclaw checks for new releases in the background (at most once a day) and nudges
335
- you when one lands. To upgrade:
335
+ you when one lands. To bring a project up to date (migrations only — never runs
336
+ `npm install -g`):
336
337
 
337
338
  ```bash
338
339
  speclaw update
339
340
  ```
340
341
 
341
- `update` upgrades the global package **and** brings the current project up to date
342
- without a re-init, splitting files by who owns them:
342
+ If a newer binary is on npm, `update` prints an advisory (`current → latest`) and
343
+ how to upgrade the binary separately (`npm i -g @esneiderbravo/speclaw@latest`, or
344
+ prefer `npx @esneiderbravo/speclaw@latest update`), then still migrates the project.
345
+
346
+ `update` brings the current project up to date without a re-init, splitting files
347
+ by who owns them:
343
348
 
344
349
  - **Managed files** (skills/commands/rules under `ai-specs/`) are **refreshed**.
345
350
  Pass `--backup` to keep a `<file>.bak` before overwrite.
@@ -348,7 +353,8 @@ without a re-init, splitting files by who owns them:
348
353
  prints a prompt for the agent you're using.
349
354
  - **`speclaw.lock`** and the CODEOWNERS owners block are refreshed when configured.
350
355
 
351
- - `speclaw update --check` — report whether an update exists, change nothing.
356
+ - `speclaw update --check` — report version status only; do not migrate.
357
+ - `speclaw update --migrate-only` — silent no-op alias of the default (compat).
352
358
  - `NO_UPDATE_NOTIFIER=1` — silence the reminder.
353
359
 
354
360
  <br/>
@@ -45,7 +45,8 @@ export async function runInit(flags) {
45
45
  const upd = await checkForUpdates();
46
46
  if (upd.updateAvailable && upd.latest) {
47
47
  ui.warn(`You're on ${c.muted(upd.current)} — latest is ${c.bold(c.cyan(upd.latest))}.`);
48
- ui.info(`Recommended: run ${ui.code("speclaw update")} first, then ${ui.code("speclaw init")} again.`);
48
+ ui.info(`Recommended: refresh the binary with ${ui.code("npx @esneiderbravo/speclaw@latest init")} ` +
49
+ `(or ${ui.code("npm i -g @esneiderbravo/speclaw@latest")}), then run ${ui.code("speclaw init")} again.`);
49
50
  ui.plain();
50
51
  }
51
52
  if (interactive) {
@@ -1,4 +1,3 @@
1
- import { spawnSync } from "node:child_process";
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import { ui, c } from "../lib/ui.js";
@@ -11,6 +10,17 @@ import { readManifest } from "../../shared/manifest.js";
11
10
  import { loadPacks } from "../../modules/tools/packs.js";
12
11
  import { detectProjectName } from "./init.js";
13
12
  import { reportTrackedLocalContent } from "../lib/untrack.js";
13
+ /**
14
+ * How to upgrade the installed binary without this command spawning `npm install -g`.
15
+ *
16
+ * @param name - Package name on npm (usually `pkgName()`).
17
+ * @returns One-line advisory for the terminal.
18
+ */
19
+ export function binaryUpgradeHint(name) {
20
+ return (`Upgrade the binary separately with ${ui.code(`npm i -g ${name}@latest`)} ` +
21
+ `(or prefer ${ui.code(`npx ${name}@latest update`)}). ` +
22
+ `This command only migrates the project.`);
23
+ }
14
24
  // The first migration must be tagged at the version that introduces this
15
25
  // mechanism (0.1.12): `isNewer` is strict, so an entry tagged at an already-
16
26
  // shipped version (e.g. 0.1.11) would never fire for projects already on it.
@@ -196,70 +206,56 @@ const MIGRATIONS = [
196
206
  "`esneiderbravo/speclaw@v2`. See `docs/cortex.md`.\n" +
197
207
  "- Preserve all project-specific wording; only apply these speclaw-authored changes.",
198
208
  },
209
+ {
210
+ version: "2.0.1",
211
+ describe: "speclaw update no longer upgrades the global npm package",
212
+ agentPrompt: "- `speclaw update` applies **project** migrations only — it does **not** run " +
213
+ "`npm install -g`. If a newer binary exists on npm it prints an advisory; " +
214
+ "upgrade the CLI separately (`npm i -g @esneiderbravo/speclaw@latest` or " +
215
+ "`npx @esneiderbravo/speclaw@latest update`). Prefer `npx @…@latest` when the " +
216
+ "installed binary is stale so migrations match the latest package.\n" +
217
+ "- Preserve all project-specific wording; only apply these speclaw-authored changes.",
218
+ },
199
219
  ];
200
220
  /**
201
- * Update speclaw and bring the current project up to date without a full re-init:
202
- * upgrade the global package, then additively apply any new standards, skills,
203
- * commands, and feature steps this project is missing (existing files untouched).
221
+ * Bring the current project up to date without a full re-init: check whether a
222
+ * newer binary exists on npm (advisory only — never runs `npm install -g`), then
223
+ * additively apply any new standards, skills, commands, and feature steps this
224
+ * project is missing (existing files untouched).
204
225
  *
205
- * @param flags - `--check` reports without changing anything; `--migrate-only`
206
- * skips the global upgrade and only applies project changes (used internally
207
- * after the package is upgraded, so migrations run from the new version).
226
+ * @param flags - `--check` reports version status only (no migrate);
227
+ * `--migrate-only` is a silent no-op alias of the default (compat).
228
+ * @param hooks - Optional test seams for the version check and migrate path.
208
229
  */
209
- export async function runUpdate(flags) {
230
+ export async function runUpdate(flags, hooks = {}) {
210
231
  const cwd = process.cwd();
211
- const migrateOnly = Boolean(flags["migrate-only"]);
232
+ // `--migrate-only` is accepted for compat but is a no-op alias of default
233
+ // (version check + project migrations). It no longer skips the version check.
212
234
  const checkOnly = Boolean(flags.check);
213
235
  const backup = Boolean(flags.backup);
214
- const winShell = process.platform === "win32";
215
- if (!migrateOnly) {
216
- ui.step("Checking for updates");
217
- const { current, latest, updateAvailable } = await checkForUpdates({ force: true });
218
- if (!latest) {
219
- ui.warn("Could not reach the npm registry — skipping the version check.");
220
- }
221
- else if (updateAvailable) {
222
- ui.info(`${c.muted(current)} ${c.muted("→")} ${c.cyan(latest)}`);
223
- if (checkOnly) {
224
- ui.info(`Run ${ui.code("speclaw update")} to upgrade and apply what's new.`);
225
- return;
226
- }
227
- ui.step(`Updating ${pkgName()} globally`);
228
- const install = spawnSync("npm", ["install", "-g", `${pkgName()}@latest`], {
229
- stdio: "inherit",
230
- shell: winShell,
231
- });
232
- if (install.status !== 0) {
233
- ui.err("Global update failed. Try again with elevated permissions (e.g. sudo), or check your npm setup.");
234
- process.exit(1);
235
- }
236
- ui.ok(`Updated to ${latest}`);
237
- // Re-exec the NEWLY installed binary so migrations run with the new assets
238
- // and any new feature steps — not this (now-stale) process. Carry --backup
239
- // through so the refresh honors it after the upgrade.
240
- const reArgs = [
241
- "update",
242
- "--migrate-only",
243
- ...(backup ? ["--backup"] : []),
244
- ...(flags.minimal ? ["--minimal"] : []),
245
- ];
246
- const re = spawnSync("speclaw", reArgs, {
247
- stdio: "inherit",
248
- shell: winShell,
249
- });
250
- if (re.error) {
251
- ui.warn(`Upgraded — now run ${ui.code("speclaw update --migrate-only")} to apply project changes.`);
252
- return;
253
- }
254
- process.exit(re.status ?? 0);
255
- }
256
- else {
257
- ui.ok(`Already on the latest version (${current}).`);
258
- if (checkOnly)
259
- return;
236
+ const check = hooks.checkForUpdates ?? checkForUpdates;
237
+ const migrate = hooks.applyProjectMigrations ?? applyProjectMigrations;
238
+ ui.step("Checking for updates");
239
+ const { current, latest, updateAvailable } = await check({ force: true });
240
+ if (!latest) {
241
+ ui.warn("Could not reach the npm registry — skipping the version check.");
242
+ }
243
+ else if (updateAvailable) {
244
+ ui.info(`${c.muted(current)} ${c.muted("→")} ${c.cyan(latest)}`);
245
+ ui.info(binaryUpgradeHint(pkgName()));
246
+ if (checkOnly) {
247
+ ui.info(`Run ${ui.code("speclaw update")} to apply project migrations with the binary you have.`);
248
+ return;
260
249
  }
261
250
  }
262
- applyProjectMigrations(cwd, backup, flags.minimal ? true : undefined);
251
+ else {
252
+ ui.ok(`Already on the latest version (${current}).`);
253
+ if (checkOnly)
254
+ return;
255
+ }
256
+ if (checkOnly)
257
+ return;
258
+ migrate(cwd, backup, flags.minimal ? true : undefined);
263
259
  }
264
260
  /**
265
261
  * Additively apply the current version's content and feature steps to a project.
package/dist/cli/index.js CHANGED
@@ -12,8 +12,8 @@ Install globally so the command is always available:
12
12
  Setup
13
13
  init Interactive setup: pick agents, scaffold, index, get the prompt
14
14
  (--minimal omits setup/lifecycle MCP tools)
15
- update Upgrade speclaw and apply only what's new (no re-init)
16
- (--minimal persists minimal exposure; omit to keep prior)
15
+ update Apply project migrations (advisory if a newer binary exists)
16
+ (--check reports version only; --minimal persists minimal exposure)
17
17
  agent list Show which agents are configured
18
18
  agent add <id> Configure another agent later (symlinks + MCP)
19
19
 
@@ -102,15 +102,16 @@ export function npmPackageUrl(name) {
102
102
  /**
103
103
  * Build the two-line "update available" notice. The latest version is rendered
104
104
  * as a clickable link to the package's npm page, so a capable terminal lets the
105
- * user open the release with a single click while `speclaw update` remains the
106
- * command that performs the upgrade.
105
+ * user open the release with a single click. Project migrations stay
106
+ * `speclaw update`; the binary is upgraded separately (prefer npx).
107
107
  *
108
108
  * @param current - The installed version.
109
109
  * @param latest - The newest published version.
110
110
  * @returns The formatted, styled notice (no leading/trailing blank lines).
111
111
  */
112
112
  export function upgradeNotice(current, latest) {
113
- const latestLink = c.cyan(link(latest, npmPackageUrl(pkgName())));
113
+ const name = pkgName();
114
+ const latestLink = c.cyan(link(latest, npmPackageUrl(name)));
114
115
  return (" " +
115
116
  c.amber("⬆ speclaw ") +
116
117
  c.muted(current + " → ") +
@@ -118,9 +119,11 @@ export function upgradeNotice(current, latest) {
118
119
  c.muted(" available") +
119
120
  "\n" +
120
121
  " " +
121
- c.muted("run ") +
122
+ c.muted("prefer ") +
123
+ c.cyan(`npx ${name}@latest update`) +
124
+ c.muted(" — or ") +
122
125
  c.cyan("speclaw update") +
123
- c.muted(" — upgrades and applies only what's new"));
126
+ c.muted(" to migrate this project"));
124
127
  }
125
128
  /**
126
129
  * Print the "update available" notice to stderr when a newer version exists.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esneiderbravo/speclaw",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },