@invisibleloop/pulse 0.2.8 → 0.2.10

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/CLAUDE.md CHANGED
@@ -454,3 +454,5 @@ First-visit JS = runtime chunk (~3.8 kB) + per-page boot file (~0.4–0.9 kB). O
454
454
  Lighthouse: 100/100/100 (Accessibility / Best Practices / SEO) on both pages.
455
455
 
456
456
  LCP is fast by design (streaming SSR sends HTML before data resolves) but actual millisecond values depend on machine speed, browser, network conditions, and server location — do not quote specific numbers.
457
+
458
+ **Caveat — third-party ads:** Pages that load ad network scripts will not achieve 100 across all categories. Ad scripts introduce render-blocking JS, third-party cookies (Best Practices), and late-loading content that causes CLS. Reserving fixed space for ad slots reduces CLS but cannot fully compensate for what ad networks inject at runtime. Treat 100/100/100 as the baseline for ad-free pages only.
@@ -34,6 +34,7 @@ export default {
34
34
  ]
35
35
  )}
36
36
  ${callout('note', 'Run <code>mcp__chrome-devtools__lighthouse_audit</code> after every new page to verify all four scores are 100. Fix any failures before considering the task done.')}
37
+ ${callout('warning', '<strong>Third-party ads caveat:</strong> Pages that load ad network scripts will not achieve 100 across all categories. Ad scripts introduce render-blocking JS, third-party cookies (Best Practices), and late-loading content that causes CLS. Reserving fixed space for ad slots reduces CLS but cannot fully compensate for what ad networks inject at runtime. Treat 100/100/100 as the baseline for ad-free pages only.')}
37
38
 
38
39
  ${section('streaming-ssr', 'Streaming SSR')}
39
40
  <p>Pulse uses Node.js streams for SSR. The server sends the <code>&lt;head&gt;</code> and page shell immediately, before any async data resolves. Browsers start downloading CSS and fonts while the server fetches data — so the user sees a styled shell within milliseconds.</p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invisibleloop/pulse",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
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.2.8
1
+ 0.2.10
@@ -0,0 +1,7 @@
1
+ Build this Pulse project for production.
2
+
3
+ ```bash
4
+ pulse build
5
+ ```
6
+
7
+ This bundles all pages into `public/dist/`. When complete, confirm the build succeeded and list the generated files.
@@ -0,0 +1,7 @@
1
+ Start (or restart) the Pulse dev server for this project.
2
+
3
+ ```bash
4
+ pulse stop; pulse dev
5
+ ```
6
+
7
+ The server port is read from `pulse.config.js` (defaults to 3000).
@@ -0,0 +1,9 @@
1
+ Start the Pulse production server for this project.
2
+
3
+ First ensure a build exists (`public/dist/`). Then run:
4
+
5
+ ```bash
6
+ pulse start
7
+ ```
8
+
9
+ The production server uses the port from `pulse.config.js` (defaults to 3000).
@@ -0,0 +1,5 @@
1
+ Stop the Pulse dev server for this project.
2
+
3
+ ```bash
4
+ pulse stop
5
+ ```
package/src/cli/dev.js CHANGED
@@ -71,6 +71,16 @@ const PUBLIC_DIR = path.join(ROOT, 'public')
71
71
  fs.copyFileSync(checklistSrc, checklistDst)
72
72
  }
73
73
 
74
+ // Sync slash commands into .claude/commands/ so /pulse-dev etc. always exist
75
+ const commandsSrc = new URL('../agent/commands', import.meta.url).pathname
76
+ const commandsDst = path.join(ROOT, '.claude', 'commands')
77
+ if (fs.existsSync(commandsSrc)) {
78
+ fs.mkdirSync(commandsDst, { recursive: true })
79
+ for (const file of fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'))) {
80
+ fs.copyFileSync(path.join(commandsSrc, file), path.join(commandsDst, file))
81
+ }
82
+ }
83
+
74
84
  console.log(` ✓ pulse-ui assets synced (v${pkgVersion})\n`)
75
85
  })()
76
86
 
package/src/cli/index.js CHANGED
@@ -289,7 +289,7 @@ async function runUpdate(root) {
289
289
  const dst = path.join(publicDir, asset)
290
290
  if (!fs.existsSync(src)) { missing.push(asset); continue }
291
291
  fs.copyFileSync(src, dst)
292
- updated.push(asset)
292
+ updated.push(`public/${asset}`)
293
293
  }
294
294
 
295
295
  // Sync agent checklist into .claude/
@@ -301,12 +301,23 @@ async function runUpdate(root) {
301
301
  updated.push('.claude/pulse-checklist.md')
302
302
  }
303
303
 
304
+ // Sync slash commands into .claude/commands/
305
+ const commandsSrc = new URL('../agent/commands', import.meta.url).pathname
306
+ const commandsDst = path.join(root, '.claude', 'commands')
307
+ if (fs.existsSync(commandsSrc)) {
308
+ fs.mkdirSync(commandsDst, { recursive: true })
309
+ for (const file of fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'))) {
310
+ fs.copyFileSync(path.join(commandsSrc, file), path.join(commandsDst, file))
311
+ updated.push(`.claude/commands/${file}`)
312
+ }
313
+ }
314
+
304
315
  // Read the new version for the success message
305
316
  const versionFile = path.join(publicDir, '.pulse-ui-version')
306
317
  const version = fs.existsSync(versionFile) ? fs.readFileSync(versionFile, 'utf8').trim() : '?'
307
318
 
308
- console.log(`\n⚡ Pulse UI updated to ${version}\n`)
309
- for (const f of updated) console.log(` ✓ public/${f}`)
319
+ console.log(`\n⚡ Pulse updated to ${version}\n`)
320
+ for (const f of updated) console.log(` ✓ ${f}`)
310
321
  for (const f of missing) console.log(` ✗ ${f} not found in package`)
311
322
  console.log()
312
323
  }
@@ -85,6 +85,14 @@ ${port !== 3000 ? ` port: ${port},\n` : ''}}
85
85
  // CLAUDE.md — in .claude/ so it's alongside Claude Code's own config, not cluttering the project root
86
86
  write(targetDir, '.claude/CLAUDE.md', claudeMd(name))
87
87
 
88
+ // Slash commands — copied from src/agent/commands/ so scaffold and dev sync stay in lock-step
89
+ const commandsSrc = new URL('../agent/commands', import.meta.url).pathname
90
+ if (fs.existsSync(commandsSrc)) {
91
+ for (const file of fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'))) {
92
+ write(targetDir, `.claude/commands/${file}`, fs.readFileSync(path.join(commandsSrc, file), 'utf8'))
93
+ }
94
+ }
95
+
88
96
  // settings.json — hooks that enforce correct agent behaviour
89
97
  write(targetDir, '.claude/settings.json', JSON.stringify({
90
98
  hooks: {
@@ -408,3 +416,4 @@ function write(dir, relPath, content) {
408
416
  fs.mkdirSync(path.dirname(filePath), { recursive: true })
409
417
  fs.writeFileSync(filePath, content, 'utf8')
410
418
  }
419
+