@invisibleloop/pulse 0.20.40 → 0.20.42

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invisibleloop/pulse",
3
- "version": "0.20.40",
3
+ "version": "0.20.42",
4
4
  "type": "module",
5
5
  "description": "AI-first frontend framework. The spec is the source of truth.",
6
6
  "license": "MIT",
@@ -61,7 +61,7 @@
61
61
  "scripts": {
62
62
  "dev": "node --watch src/cli/dev.js",
63
63
  "build": "node scripts/build.js",
64
- "test": "node src/spec/schema.test.js && node src/runtime/runtime.test.js && node src/runtime/navigate.test.js && node src/runtime/morph.test.js && node src/runtime/ssr.test.js && node src/runtime/image.test.js && node src/server/server.test.js && node src/cli/cli.test.js && node src/cli/validate.test.js && node src/cli/diagnose.test.js && node src/cli/check-bundles.test.js && node src/cli/review.test.js && node src/cli/process-utils.test.js && node src/cli/stamp.test.js && node src/cli/feedback.test.js && node src/ui/ui.test.js && node src/testing/testing.test.js && node src/store/store.test.js && node src/md/md.test.js && node scripts/strip-server.test.js && node scripts/css-purge.test.js && node src/mcp/mcp.test.js && node src/mcp/guards.test.js",
64
+ "test": "node src/spec/schema.test.js && node src/runtime/runtime.test.js && node src/runtime/navigate.test.js && node src/runtime/morph.test.js && node src/runtime/ssr.test.js && node src/runtime/image.test.js && node src/server/server.test.js && node src/cli/cli.test.js && node src/cli/validate.test.js && node src/cli/diagnose.test.js && node src/cli/check-bundles.test.js && node src/cli/review.test.js && node src/cli/process-utils.test.js && node src/cli/stamp.test.js && node src/cli/feedback.test.js && node src/cli/update.test.js && node src/ui/ui.test.js && node src/testing/testing.test.js && node src/store/store.test.js && node src/md/md.test.js && node scripts/strip-server.test.js && node scripts/css-purge.test.js && node src/mcp/mcp.test.js && node src/mcp/guards.test.js",
65
65
  "typecheck": "tsc --noEmit",
66
66
  "docs": "node --watch src/cli/dev.js --root ../../docs --port 4000",
67
67
  "docs:start": "node ../../docs/server.js"
@@ -1 +1 @@
1
- 0.20.40
1
+ 0.20.42
package/src/cli/index.js CHANGED
@@ -13,7 +13,8 @@
13
13
  * pulse stamp [--route </path>] [--full|--quick] write .pulse-verified — same check pulse_stamp (MCP) runs
14
14
  * pulse check-bundles inspect public/dist/ bundle contents (requires prior build)
15
15
  * pulse review <file> [--quick] run the mechanized review checklist — same check pulse_review (MCP) runs
16
- * pulse update re-copy pulse-ui.css/js from installed package → public/
16
+ * pulse update upgrade @invisibleloop/pulse to latest, then re-copy its assets → public/
17
+ * (--no-install skips the upgrade and only re-copies)
17
18
  * pulse --version print installed version and exit
18
19
  * pulse -v alias for --version
19
20
  * pulse --help show usage and exit
@@ -25,6 +26,7 @@ import path from 'path'
25
26
  import fs from 'fs'
26
27
  import { scaffold } from './scaffold.js'
27
28
  import { killPulseServerOnPort } from './process-utils.js'
29
+ import { isInstalledInProject, installCommand, PACKAGE } from './update.js'
28
30
 
29
31
  // Extract --agent flag before routing, so it works alongside any subcommand
30
32
  const rawArgs = process.argv.slice(2)
@@ -485,6 +487,24 @@ async function runUpdate(root) {
485
487
  process.exit(1)
486
488
  }
487
489
 
490
+ // Upgrade first, when this CLI is the copy installed in the project. The
491
+ // running process still holds the old code, so hand the copy step to the
492
+ // freshly installed binary. A global install or a dev `npm link` is left
493
+ // alone and just re-copies, as before.
494
+ const pkgDir = new URL('../..', import.meta.url).pathname.replace(/\/$/, '')
495
+ if (!args.includes('--no-install') && isInstalledInProject(pkgDir, root)) {
496
+ const { spawnSync } = await import('child_process')
497
+ const [cmd, cmdArgs] = installCommand(root)
498
+ console.log(`\n Upgrading ${PACKAGE} (${cmd} ${cmdArgs.join(' ')})…\n`)
499
+ const install = spawnSync(cmd, cmdArgs, { cwd: root, stdio: 'inherit' })
500
+ if (install.status !== 0) {
501
+ console.error(`\n Upgrade failed — nothing changed. Fix the error above, or run \`pulse update --no-install\` to only re-copy assets.\n`)
502
+ process.exit(install.status || 1)
503
+ }
504
+ const next = spawnSync(process.execPath, [path.join(pkgDir, 'src', 'cli', 'index.js'), 'update', '--no-install'], { cwd: root, stdio: 'inherit' })
505
+ process.exit(next.status ?? 1)
506
+ }
507
+
488
508
  // Copy assets from the package this CLI binary lives in (import.meta.url).
489
509
  // When globally npm-linked to the dev repo, this resolves to dev source.
490
510
  // When installed normally, this resolves to the installed version.
@@ -619,7 +639,7 @@ switch (command) {
619
639
  ${c.cyan('stamp')} write .pulse-verified ${c.dim('[--route </path>] [--full|--quick]')}
620
640
  ${c.cyan('check-bundles')} inspect public/dist/ bundle contents ${c.dim('(requires prior build)')}
621
641
  ${c.cyan('review')} ${c.dim('<file>')} run the mechanized review checklist ${c.dim('[--quick]')}
622
- ${c.cyan('update')} re-copy pulse-ui assets from installed package
642
+ ${c.cyan('update')} upgrade to the latest version, then re-copy pulse-ui assets ${c.dim('[--no-install]')}
623
643
 
624
644
  ${c.bold('Options:')}
625
645
  ${c.cyan('--agent')} ${c.dim('<name>')} use specific agent ${c.dim('(claude | copilot)')}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Pulse — helpers for `pulse update`.
3
+ *
4
+ * `pulse update` first upgrades the package in the project, then re-copies its
5
+ * assets. Only the first step is decided here; the copy lives in index.js.
6
+ */
7
+
8
+ import fs from 'fs'
9
+ import path from 'path'
10
+
11
+ export const PACKAGE = '@invisibleloop/pulse'
12
+
13
+ /**
14
+ * True when the running CLI is the copy installed in `root/node_modules`.
15
+ * A global install or an `npm link` to a dev checkout resolves elsewhere, and
16
+ * must not be "upgraded" through the project's package.json.
17
+ */
18
+ export function isInstalledInProject(pkgDir, root) {
19
+ try {
20
+ const installed = fs.realpathSync(path.join(root, 'node_modules', PACKAGE))
21
+ if (fs.realpathSync(pkgDir) !== installed) return false
22
+ // pnpm symlinks into node_modules/.pnpm; an `npm link` points outside it.
23
+ const modules = fs.realpathSync(path.join(root, 'node_modules'))
24
+ return installed.startsWith(modules + path.sep)
25
+ } catch {
26
+ return false
27
+ }
28
+ }
29
+
30
+ /** [command, args] that upgrades the package with the project's package manager. */
31
+ export function installCommand(root) {
32
+ const has = f => fs.existsSync(path.join(root, f))
33
+ if (has('pnpm-lock.yaml')) return ['pnpm', ['add', `${PACKAGE}@latest`]]
34
+ if (has('yarn.lock')) return ['yarn', ['add', `${PACKAGE}@latest`]]
35
+ return ['npm', ['install', `${PACKAGE}@latest`]]
36
+ }