@invisibleloop/pulse 0.1.29 → 0.1.30

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.
@@ -23,12 +23,13 @@ jobs:
23
23
 
24
24
  - run: npm test
25
25
 
26
- - name: Publish if version is new
26
+ - name: Bump patch version if already published, then publish
27
+ env:
28
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
27
29
  run: |
28
30
  VERSION=$(node -p "require('./package.json').version")
29
- EXISTS=$(npm view @invisibleloop/pulse@$VERSION version 2>/dev/null || echo "")
30
- if [ -z "$EXISTS" ]; then
31
- npm publish --provenance --access public
32
- else
33
- echo "Version $VERSION already published — skipping."
34
- fi
31
+ while npm view @invisibleloop/pulse@$VERSION version 2>/dev/null | grep -q .; do
32
+ VERSION=$(node -e "const [a,b,c]='$VERSION'.split('.').map(Number);console.log(a+'.'+b+'.'+(c+1))")
33
+ done
34
+ npm version $VERSION --no-git-tag-version
35
+ npm publish --provenance --access public
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Pulse
2
2
 
3
- **A spec-first, AI-native web framework.** Early access — v0.1.
3
+ **A spec-first, AI-native web framework.** Early access — v0.1.30.
4
4
 
5
5
  Write a plain JavaScript object that describes what a page does. Pulse handles routing, SSR, hydration, client-side navigation, compression, security headers, and caching automatically.
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invisibleloop/pulse",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "type": "module",
5
5
  "description": "AI-first frontend framework. The spec is the source of truth.",
6
6
  "license": "MIT",
@@ -810,8 +810,8 @@ hr.ui-divider {
810
810
  }
811
811
 
812
812
  .ui-testimonial-avatar--initials {
813
- background: var(--ui-accent-dim, rgba(155,141,255,.12));
814
- color: var(--ui-accent, var(--accent, #9b8dff));
813
+ background: var(--ui-muted-bg, rgba(128,128,128,.2));
814
+ color: var(--ui-text, var(--text, #e2e2ea));
815
815
  font-size: .8rem;
816
816
  font-weight: 700;
817
817
  display: flex;
@@ -151,7 +151,7 @@ Use `prose()` for HTML from external sources (CMS, markdown). Use `heading()`/`l
151
151
  | `feature` | `icon` (HTML slot), `title`, `level` (1–6, default 3), `description`, `center` |
152
152
  | `testimonial` | `quote`, `name`, `role`, `src`, `rating` (1–5) |
153
153
  | `pricing` | `name`, `level` (1–6, default 3), `price`, `period`, `features` ([strings]), `action` (HTML slot), `highlighted` |
154
- | `accordion` | `items` ([{title,content}]) — native `<details>`, no JS |
154
+ | `accordion` | `items` ([{question,answer}]) — native `<details>`, no JS |
155
155
  | `appBadge` | `store` (apple/google), `href` |
156
156
  | `cta` | `eyebrow`, `title`, `level` (1–6, default 2), `subtitle`, `actions` (HTML slot), `align` |
157
157
 
@@ -75,6 +75,7 @@ export default {
75
75
  ## Key rules
76
76
 
77
77
  - view() returns an HTML string using template literals
78
+ - **Component props are auto-escaped — pass literal strings, not HTML entities.** Use `'© 2026 Acme'` not `'&copy; 2026 Acme'`. Use `'Pricing & Plans'` not `'Pricing &amp; Plans'`. Passing HTML entities to component props double-escapes them (`&amp;amp;`, `&amp;copy;`) and shows raw entity text to the user.
78
79
  - data-event="mutationName" on buttons/elements — passes the DOM event to the mutation
79
80
  - data-event="change:mutationName" to fire on input change
80
81
  - data-action="actionName" on <form> elements only — submits pass FormData to the action
@@ -73,7 +73,20 @@ All components use `--ui-font` (body) and `--ui-mono` (code). These resolve from
73
73
 
74
74
  ### Google Fonts
75
75
 
76
- Add the Google Fonts stylesheet URL **before** `pulse-ui.css` in `meta.styles`. Use `family=Name:wght@weights` and always include `&display=swap`.
76
+ **Google Fonts requires CSP configuration** — the default CSP blocks external font and stylesheet sources. You must pass `csp` to `createServer`, otherwise the font will be blocked and the page will fail Lighthouse Best Practices:
77
+
78
+ ```js
79
+ // server.js
80
+ createServer(specs, {
81
+ port: 3000,
82
+ csp: {
83
+ 'style-src': ['https://fonts.googleapis.com'],
84
+ 'font-src': ['https://fonts.gstatic.com'],
85
+ },
86
+ })
87
+ ```
88
+
89
+ Then add the URL **before** `pulse-ui.css` in `meta.styles`. Use `family=Name:wght@weights` and always include `&display=swap`.
77
90
 
78
91
  ```js
79
92
  meta: {
@@ -90,6 +103,8 @@ Then in app.css:
90
103
  :root { --font: 'Inter', system-ui, sans-serif; }
91
104
  ```
92
105
 
106
+ If you cannot modify `createServer` (e.g. the project uses auto-discovery), use a self-hosted font in `public/fonts/` with `@font-face` in `app.css` — no CSP changes needed.
107
+
93
108
  For multiple weights or italic variants, separate them with a semicolon in the URL:
94
109
  ```
95
110
  ?family=Inter:ital,wght@0,400;0,700;1,400&display=swap
@@ -33,7 +33,7 @@ Wait for the user to confirm or adjust the plan before writing any code.
33
33
 
34
34
  ## Phase 3 — Build
35
35
 
36
- Write the spec and any related files (components, styles, tests skeleton). Follow the checklist in full. After each file is written, output a one-line status: `✓ Page written — validating...`
36
+ Write the spec and any related files using the **Write tool** — not `pulse_create_page`. This shows the user a readable diff of the file content. After writing, call `pulse_create_page(name)` to validate the file you just wrote. After each file is written, output a one-line status: `✓ Page written — validating...`
37
37
 
38
38
  ---
39
39
 
package/src/mcp/server.js CHANGED
@@ -233,25 +233,27 @@ server.registerTool(
233
233
  server.registerTool(
234
234
  'pulse_create_page',
235
235
  {
236
- description: `Create a new page in the Pulse project. Filename determines route: home.js /, about.js → /about.
236
+ description: `Validate and register a page spec that you have already written to disk with the Write tool.
237
237
 
238
- IMPORTANT: Always follow these rules when writing the spec content:
238
+ Workflow always in this order:
239
+ 1. Write the spec file to src/pages/<name>.js using the Write tool (user sees the diff)
240
+ 2. Call pulse_create_page with just the name to validate it
241
+
242
+ Do NOT pass content here — write the file first, then call this tool.
243
+
244
+ Rules for the spec you write:
239
245
  - Import Pulse UI components from '@invisibleloop/pulse/ui' — never write raw HTML for nav, hero, button, card, input, etc.
240
246
  - Include '/pulse-ui.css' in meta.styles whenever using any UI component
241
- - Use u- utility classes for spacing/layout (u-flex, u-flex-col, u-gap-4, u-mt-8, u-text-center, etc.) — never inline styles
242
- - Use var(--ui-*) CSS tokens in any custom CSS — never hardcode hex colours
247
+ - Use u- utility classes for spacing/layout — never inline styles
248
+ - Use var(--ui-*) CSS tokens for any colour — never hardcode hex values
243
249
  - onSuccess AND onError are both required in every action
244
250
  - Do NOT use data-event on text inputs — use FormData in onStart/run instead
245
251
  - Always export default spec`,
246
252
  inputSchema: {
247
- name: z.string().describe('Filename without extension, e.g. "about" or "blog/post"'),
248
- content: z.string().describe('Complete JS spec — must export default a valid Pulse spec object'),
253
+ name: z.string().describe('Filename without extension, matching what you wrote — e.g. "about" or "blog/post"'),
249
254
  },
250
255
  },
251
- async ({ name, content }) => {
252
- const validation = await validateContent(content)
253
- if (validation.content[0].text.startsWith('Invalid')) return validation
254
-
256
+ async ({ name }) => {
255
257
  const segments = name.replace(/\.js$/, '').split('/')
256
258
  const fullPath = path.join(PAGES_DIR, ...segments) + '.js'
257
259
 
@@ -259,11 +261,16 @@ IMPORTANT: Always follow these rules when writing the spec content:
259
261
  return text('Error: page name must not escape src/pages/')
260
262
  }
261
263
 
262
- fs.mkdirSync(path.dirname(fullPath), { recursive: true })
263
- fs.writeFileSync(fullPath, content, 'utf8')
264
+ if (!fs.existsSync(fullPath)) {
265
+ return text(`Error: ${path.relative(ROOT, fullPath)} does not exist — write the file with the Write tool first, then call pulse_create_page.`)
266
+ }
267
+
268
+ const content = fs.readFileSync(fullPath, 'utf8')
269
+ const validation = await validateContent(content)
270
+ if (validation.content[0].text.startsWith('Invalid')) return validation
264
271
 
265
272
  const route = derivedRouteFromName(name)
266
- return text(`Created ${path.relative(ROOT, fullPath)} → route "${route}"`)
273
+ return text(`Validated ${path.relative(ROOT, fullPath)} → route "${route}"`)
267
274
  }
268
275
  )
269
276
 
@@ -685,10 +692,19 @@ server.registerTool(
685
692
  server.registerTool(
686
693
  'pulse_update',
687
694
  {
688
- description: 'Re-copy pulse-ui.css, pulse-ui.js, and the agent checklist from the installed package into public/. Run after npm update @invisibleloop/pulse, or when visual output looks wrong and you suspect stale CSS.',
695
+ description: 'Install the latest @invisibleloop/pulse package, then re-copy pulse-ui.css, pulse-ui.js, and the agent checklist into public/. One command does the full upgrade.',
689
696
  inputSchema: {},
690
697
  },
691
- () => {
698
+ async () => {
699
+ // 1. npm install latest
700
+ const { execSync } = await import('child_process')
701
+ try {
702
+ execSync('npm install @invisibleloop/pulse@latest', { cwd: ROOT, stdio: 'pipe' })
703
+ } catch (e) {
704
+ return text(`npm install failed:\n${e.stderr?.toString() || e.message}`)
705
+ }
706
+
707
+ // 2. Copy assets from the newly installed package
692
708
  const pkgPublic = new URL('../../public', import.meta.url).pathname
693
709
  const publicDir = path.join(ROOT, 'public')
694
710
  const assets = ['pulse-ui.css', 'pulse-ui.js', '.pulse-ui-version']
@@ -711,7 +727,7 @@ server.registerTool(
711
727
 
712
728
  const versionFile = path.join(publicDir, '.pulse-ui-version')
713
729
  const version = fs.existsSync(versionFile) ? fs.readFileSync(versionFile, 'utf8').trim() : '?'
714
- return text(`pulse-ui updated to v${version}\n\n${updated.map(f => `✓ ${f}`).join('\n')}`)
730
+ return text(`Pulse updated to v${version}\n\n${updated.map(f => `✓ ${f}`).join('\n')}`)
715
731
  }
716
732
  )
717
733
 
@@ -826,7 +842,7 @@ const PULSE_GUIDE_INDEX = `# Pulse Framework Guide
826
842
  - \`pulse_list_structure\` — list pages, components, and pulse-ui version. Call at the start of every session.
827
843
  - \`pulse_validate\` — validate spec content. Call after every write. Fix all errors AND warnings.
828
844
  - \`pulse_review\` — switch into reviewer mode and critically examine a spec you just built. Returns the source, rendered HTML, validator output, and a full review checklist. **Call this only after validate, Lighthouse (desktop + mobile), and tests all pass — it is the final phase before declaring done.**
829
- - \`pulse_create_page\` — create a new page spec. Validates before writing.
845
+ - \`pulse_create_page\` — validate a page spec you already wrote to disk. **Always write the file with the Write tool first, then call this.** Never pass content to this tool.
830
846
  - \`pulse_create_component\` — create a reusable component.
831
847
  - \`pulse_create_store\` — create the pulse.store.js global store.
832
848
  - \`pulse_create_action\` — generate a correctly-structured action snippet.
@@ -834,7 +850,7 @@ const PULSE_GUIDE_INDEX = `# Pulse Framework Guide
834
850
  - \`pulse_restart_server\` — stop and restart the dev server.
835
851
  - \`pulse_build\` — production build + starts prod server on devPort+1 for Lighthouse. Returns the URL. Call \`pulse_restart_server\` after to return to dev. **Slow — takes 30–60 s. Tell the user before calling.**
836
852
  - \`pulse_check_version\` — check installed package version, static asset version, and latest on npm. Use this instead of running npm commands when the user asks about updates.
837
- - \`pulse_update\` — re-copy \`pulse-ui.css\`, \`pulse-ui.js\`, and the agent checklist from the installed package into \`public/\`. Run this after \`npm update @invisibleloop/pulse\`, or whenever visual output looks wrong and you suspect stale CSS.
853
+ - \`pulse_update\` — install the latest \`@invisibleloop/pulse\` package and re-copy \`pulse-ui.css\`, \`pulse-ui.js\`, and the agent checklist into \`public/\`. One command does the full upgrade.
838
854
 
839
855
  **Chrome DevTools MCP tools** (globally available):
840
856
  - \`mcp__chrome-devtools__take_screenshot\` — visual screenshot of the page.
@@ -16,7 +16,11 @@ import fs from 'fs'
16
16
  import path from 'path'
17
17
  import zlib from 'zlib'
18
18
  import crypto from 'crypto'
19
- import { promisify } from 'util'
19
+ import { promisify } from 'util'
20
+ import { createRequire } from 'module'
21
+
22
+ const _require = createRequire(import.meta.url)
23
+ export const version = _require('../../package.json').version
20
24
  import { renderToString, renderToStream, wrapDocument, resolveServerState } from '../runtime/ssr.js'
21
25
  import { validateSpec } from '../spec/schema.js'
22
26
  import { validateStore, resolveStoreState } from '../store/index.js'
package/src/ui/stat.js CHANGED
@@ -39,7 +39,7 @@ export function stat({
39
39
 
40
40
  const changeHtml = change
41
41
  ? `<p class="ui-stat-change ui-stat-change--${e(trend)}">
42
- <span aria-label="${e(TREND_LABELS[trend])}">${TREND_ICONS[trend]}</span>
42
+ <span role="img" aria-label="${e(TREND_LABELS[trend])}">${TREND_ICONS[trend]}</span>
43
43
  ${e(change)}
44
44
  </p>`
45
45
  : ''
package/src/ui/ui.test.js CHANGED
@@ -346,6 +346,12 @@ test('stat: renders change with trend class', () => {
346
346
  assert.match(html, /\+5%/)
347
347
  })
348
348
 
349
+ test('stat: trend icon span has role=img for valid aria-label', () => {
350
+ const html = stat({ label: 'X', value: '10', change: '+5%', trend: 'up' })
351
+ assert.match(html, /role="img"/)
352
+ assert.match(html, /aria-label="increase"/)
353
+ })
354
+
349
355
  test('stat: no change rendered when change is empty', () => {
350
356
  const html = stat({ label: 'X', value: '10' })
351
357
  assert.doesNotMatch(html, /ui-stat-change/)
@@ -1,113 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "mcp__chrome-devtools__navigate_page",
5
- "mcp__chrome-devtools__evaluate_script",
6
- "Bash(grep:*)",
7
- "mcp__chrome-devtools__list_console_messages",
8
- "mcp__chrome-devtools__take_screenshot",
9
- "Bash(pkill -f \"docs/server.js\")",
10
- "Bash(curl -s -o /dev/null -w \"%{http_code}\" http://localhost:4000/)",
11
- "Bash(node:*)",
12
- "Bash(for f:*)",
13
- "Bash(do echo:*)",
14
- "Read(//Users/andy.stubbs/Repos/AS/pulse2/src/ui/**)",
15
- "Bash(/dev/null done:*)",
16
- "Bash(mkdir -p /Users/andy.stubbs/Repos/AS/agent-test/.claude)",
17
- "Bash(mv /Users/andy.stubbs/Repos/AS/agent-test/CLAUDE.md /Users/andy.stubbs/Repos/AS/agent-test/.claude/CLAUDE.md)",
18
- "Read(//Users/andy.stubbs/Repos/AS/agent-test-3/public/**)",
19
- "Bash(ls /Users/andy.stubbs/Repos/AS/pulse2/public/*.css /Users/andy.stubbs/Repos/AS/pulse2/public/*.js)",
20
- "Bash(cp /Users/andy.stubbs/Repos/AS/pulse2/public/pulse-ui.css /Users/andy.stubbs/Repos/AS/agent-test-3/public/pulse-ui.css)",
21
- "Bash(cp /Users/andy.stubbs/Repos/AS/pulse2/public/pulse-ui.js /Users/andy.stubbs/Repos/AS/agent-test-3/public/pulse-ui.js)",
22
- "Bash(cp /Users/andy.stubbs/Repos/AS/pulse2/public/pulse-ui.css /Users/andy.stubbs/Repos/AS/agent-test/public/pulse-ui.css)",
23
- "Bash(cp /Users/andy.stubbs/Repos/AS/pulse2/public/pulse-ui.css /Users/andy.stubbs/Repos/AS/my-amazing-pulse-project/public/pulse-ui.css)",
24
- "Bash(npm test:*)",
25
- "Bash(npm run:*)",
26
- "Bash(git config:*)",
27
- "Bash(curl -s -o /dev/null -w \"%{http_code}\" http://localhost:4000)",
28
- "Bash(curl:*)",
29
- "Bash(xargs kill:*)",
30
- "mcp__chrome-devtools__lighthouse_audit",
31
- "Bash(git add:*)",
32
- "Bash(git commit:*)",
33
- "Bash(git push:*)",
34
- "Bash(git pull:*)",
35
- "Bash(git stash:*)",
36
- "Bash(__NEW_LINE_9ac7abf2e7b711bb__ node -e \":*)",
37
- "Bash(python3:*)",
38
- "mcp__chrome-devtools__list_pages",
39
- "mcp__chrome-devtools__emulate",
40
- "Bash(npx tsc:*)",
41
- "Bash(find /Users/andy.stubbs/.claude -name *.json)",
42
- "Bash(ls /Users/andy.stubbs/Repos/AS/pulse2/src/**/*.test.js)",
43
- "mcp__chrome-devtools__list_network_requests",
44
- "mcp__chrome-devtools__get_network_request",
45
- "Bash(find /Users/andy.stubbs/Repos/AS/forge -name *.md -o -name *.txt)",
46
- "Bash(node_modules/.bin/tsc --noEmit)",
47
- "Bash(/Users/andy.stubbs/Repos/AS/forge/node_modules/.bin/tsc --noEmit --project /Users/andy.stubbs/Repos/AS/forge/tsconfig.json)",
48
- "WebFetch(domain:localhost)",
49
- "Skill(update-config)",
50
- "Bash(jq:*)",
51
- "Bash(ls /Users/andy.stubbs/Repos/AS/tasty/.claude/ grep -r \"pulse-dev\\\\|pulse-stop\\\\|pulse-build\\\\|pulse-start\\\\|Pulse project\" /Users/andy.stubbs/Repos/AS/tasty/.claude/ 2)",
52
- "Bash(/dev/null grep:*)",
53
- "Bash(ls -1 /Users/andy.stubbs/Repos/AS/pulse2/src/ui/*.js)",
54
- "Bash(ls /Users/andy.stubbs/Repos/AS/pulse2/docs/src/pages/*.js)",
55
- "Bash(wc -c /Users/andy.stubbs/Repos/AS/pulse2/src/agent/guide-*.md)",
56
- "Bash(npx:*)",
57
- "Bash(npm install:*)",
58
- "Bash(./node_modules/.bin/tsc --noEmit --strict --moduleResolution bundler --module esnext --target esnext --allowJs false --skipLibCheck false types/schema.d.ts types/server.d.ts types/runtime.d.ts types/navigate.d.ts types/ssr.d.ts types/html.d.ts types/image.d.ts types/ui.d.ts types/index.d.ts 2>&1)",
59
- "Bash(./node_modules/.bin/tsc --noEmit --strict --moduleResolution bundler --module esnext --target esnext --allowJs false --lib esnext,dom types/schema.d.ts types/server.d.ts types/runtime.d.ts types/navigate.d.ts types/ssr.d.ts types/html.d.ts types/image.d.ts types/ui.d.ts types/index.d.ts 2>&1)",
60
- "Bash(/Users/andy.stubbs/Repos/AS/pulse2/node_modules/.bin/tsc -p /tmp/tsconfig_test.json 2>&1)",
61
- "Bash(find /Users/andy.stubbs/Repos/AS/pulse2 -name *.test.js -type f)",
62
- "Bash(wc:*)",
63
- "Bash(ls -1 *.js)",
64
- "Bash(grep -l \"<h[2-6]\" /Users/andy.stubbs/Repos/AS/pulse2/src/ui/*.js)",
65
- "Bash(node --input-type=module --eval \"import ''''/Users/andy.stubbs/Repos/AS/pulse2/src/cli/scaffold.js''''\")",
66
- "Bash(head -10 grep -n \"level\\\\|heading\" /Users/andy.stubbs/Repos/AS/pulse2/docs/src/pages/components/section.js)",
67
- "Bash(head -10 grep -n \"level\\\\|heading\" /Users/andy.stubbs/Repos/AS/pulse2/docs/src/pages/components/modal.js)",
68
- "Bash(head -10 grep -n \"table\\(\\\\|''''Prop''''\\\\|flush\\\\|center\\\\|highlighted\\\\|eyebrow\\\\|align\\\\|size.*md\\\\|ui-modal\" /Users/andy.stubbs/Repos/AS/pulse2/docs/src/pages/components/modal.js)",
69
- "Bash(head -10 grep -n \"table\\(\\\\|''''Prop''''\\\\|eyebrow\\\\|padding\\\\|variant\\\\|subtitle\" /Users/andy.stubbs/Repos/AS/pulse2/docs/src/pages/components/section.js)",
70
- "Bash(head -10 grep -n \"table\\(\\\\|''''Prop''''\\\\|highlighted\\\\|badge\\\\|period\\\\|features\" /Users/andy.stubbs/Repos/AS/pulse2/docs/src/pages/components/pricing.js)",
71
- "Bash(head -10 grep -n \"table\\(\\\\|''''Prop''''\\\\|eyebrow\\\\|subtitle\\\\|actions\\\\|align\" /Users/andy.stubbs/Repos/AS/pulse2/docs/src/pages/components/cta.js)",
72
- "Bash(echo \"exit: $?\")",
73
- "Bash(xargs node:*)",
74
- "Bash(find /Users/andy.stubbs/Repos/AS/ultimate-demo/twizzer -name server.js -o -name *.config.*)",
75
- "Bash(xargs ls:*)",
76
- "Bash(xargs -I{} lsof -p {})",
77
- "Bash(brotli --best -c /Users/andy.stubbs/Repos/AS/ultimate-demo/twizzer/public/dist/home.boot-TJOPUIR4.js)",
78
- "WebSearch",
79
- "WebFetch(domain:github.com)",
80
- "Bash(ls:*)",
81
- "Bash(git -C /Users/andy.stubbs/Repos/AS/pulse2 log --follow --diff-filter=A --format=\"%ar\" -- public/dist/runtime-YLZ4ZGK4.js)",
82
- "Read(//Users/andy.stubbs/Repos/AS/pulse2/**)",
83
- "Bash(GetFileInfo /Users/andy.stubbs/Repos/AS/pulse2/public/dist/runtime-YLZ4ZGK4.js)",
84
- "mcp__chrome-devtools__performance_start_trace",
85
- "Bash(pkill -f docs/server.js)",
86
- "Bash(sleep 1 node -e \":*)",
87
- "mcp__chrome-devtools__close_page",
88
- "mcp__chrome-devtools__fill",
89
- "mcp__chrome-devtools__click",
90
- "mcp__chrome-devtools__take_snapshot",
91
- "mcp__chrome-devtools__type_text",
92
- "Bash(head -5 grep -n \"section\\\\|container\" /Users/andy.stubbs/Repos/AS/pulse2/src/ui/index.js)",
93
- "Bash(head -35 grep \"ui-section-header\\\\|ui-section-title\\\\|ui-section-eyebrow\\\\|ui-section--\" /Users/andy.stubbs/Repos/AS/pulse2/public/pulse-ui.css)",
94
- "mcp__chrome-devtools__performance_analyze_insight",
95
- "Bash(git checkout:*)",
96
- "Bash(git branch:*)",
97
- "Bash(gh run:*)",
98
- "Bash(xargs -I{} gh run delete {} --repo invisibleloop/pulse-framework)",
99
- "Bash(gh api:*)",
100
- "Bash(npm access:*)",
101
- "Bash(gh pr:*)",
102
- "Bash(git fetch:*)",
103
- "Bash(git rebase:*)",
104
- "Bash(git rm:*)",
105
- "Bash(sed -i '' 's/Ship with it built in/Production quality, built in/' docs/src/pages/home.js)",
106
- "Bash(3 \" -A2 docs/src/pages/home.js)",
107
- "Bash(git merge:*)",
108
- "Bash(node -p \"require\\(''./package.json''\\).version\")",
109
- "Bash(npm view:*)",
110
- "Bash(git cherry-pick:*)"
111
- ]
112
- }
113
- }