@invisibleloop/pulse 0.20.3 → 0.20.5

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.
@@ -6,6 +6,9 @@ on:
6
6
  pull_request:
7
7
  branches: [main]
8
8
 
9
+ permissions:
10
+ contents: read
11
+
9
12
  jobs:
10
13
  test:
11
14
  runs-on: ubuntu-latest
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invisibleloop/pulse",
3
- "version": "0.20.3",
3
+ "version": "0.20.5",
4
4
  "type": "module",
5
5
  "description": "AI-first frontend framework. The spec is the source of truth.",
6
6
  "license": "MIT",
@@ -1 +1 @@
1
- 0.20.3
1
+ 0.20.5
package/src/mcp/server.js CHANGED
@@ -37,6 +37,20 @@ import { execFileSync, spawn, spawnSync } from 'child_process'
37
37
 
38
38
  import { loadPages } from '../cli/discover.js'
39
39
 
40
+ // ---------------------------------------------------------------------------
41
+ // Crash guards — an uncaught error in any tool handler must not kill the
42
+ // stdio transport. Without these, Claude Code sees the process exit and
43
+ // reports "pulse: disconnected" instead of a tool-level error. stderr only —
44
+ // stdout is the JSON-RPC channel and any stray write corrupts the stream.
45
+ // ---------------------------------------------------------------------------
46
+
47
+ process.on('uncaughtException', (err) => {
48
+ console.error('[pulse-mcp] uncaught exception:', err?.stack || err)
49
+ })
50
+ process.on('unhandledRejection', (err) => {
51
+ console.error('[pulse-mcp] unhandled rejection:', err?.stack || err)
52
+ })
53
+
40
54
  // ---------------------------------------------------------------------------
41
55
  // Project root
42
56
  // ---------------------------------------------------------------------------
@@ -49,6 +63,17 @@ const ROOT = rootArg !== -1
49
63
  const PAGES_DIR = path.join(ROOT, 'src', 'pages')
50
64
  const COMPONENTS_DIR = path.join(ROOT, 'src', 'components')
51
65
 
66
+ // Detect whether ROOT is actually a Pulse project — used to warn the agent
67
+ // early rather than silently misbehaving in unrelated repos.
68
+ const IS_PULSE_PROJECT = (() => {
69
+ if (fs.existsSync(path.join(ROOT, 'pulse.config.js'))) return true
70
+ if (fs.existsSync(PAGES_DIR)) return true
71
+ try {
72
+ const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'))
73
+ return !!(pkg?.dependencies?.['@invisibleloop/pulse'] || pkg?.devDependencies?.['@invisibleloop/pulse'])
74
+ } catch { return false }
75
+ })()
76
+
52
77
  const PKG_VERSION = JSON.parse(
53
78
  fs.readFileSync(new URL('../../package.json', import.meta.url).pathname, 'utf8')
54
79
  ).version
@@ -500,6 +525,9 @@ server.registerTool(
500
525
  inputSchema: {},
501
526
  },
502
527
  async () => {
528
+ if (!IS_PULSE_PROJECT) {
529
+ return { content: [{ type: 'text', text: `⚠ This does not appear to be a Pulse project (no pulse.config.js, no src/pages/, no @invisibleloop/pulse dependency found in ${ROOT}).\n\nThe pulse MCP server is project-specific — it is started by the \`pulse\` CLI from inside a Pulse project directory. If you are working on a Pulse project, check that you launched with \`pulse\` from the correct directory.` }] }
530
+ }
503
531
  const specs = await loadPages(ROOT)
504
532
  const components = findComponents()
505
533
  const lines = []
@@ -571,6 +599,9 @@ server.registerTool(
571
599
  inputSchema: {},
572
600
  },
573
601
  async () => {
602
+ if (!IS_PULSE_PROJECT) {
603
+ return { content: [{ type: 'text', text: `⚠ This does not appear to be a Pulse project (no pulse.config.js, no src/pages/, no @invisibleloop/pulse dependency found in ${ROOT}).\n\nThe pulse MCP server is project-specific — it is started by the \`pulse\` CLI from inside a Pulse project directory. If you are working on a Pulse project, check that you launched with \`pulse\` from the correct directory.` }] }
604
+ }
574
605
  const lines = []
575
606
 
576
607
  // ── Pages ──────────────────────────────────────────────────────────────