softr-vibe-coding 2.15.0 → 2.15.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/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@ All notable changes to this skill are documented here. Versions follow [Semantic
4
4
 
5
5
  Entries from 1.3.1 onward are generated automatically from git commit subjects between version bumps (see `.github/workflows/publish.yml`). Entries before 1.3.1 were backfilled by hand from the existing commit history.
6
6
 
7
+ ## [2.15.1] - 2026-10-08
8
+ - Release 2.15.1
9
+ - softr-mcp: unicode escapes come back decoded on push
10
+
7
11
  ## [2.15.0] - 2026-10-07
8
12
  - Release 2.15.0
9
13
  - Add the brand date picker reference
package/SKILL.md CHANGED
@@ -102,7 +102,7 @@ You generate complete, production-ready Softr Vibe Coding blocks as TypeScript R
102
102
  - Static block: no hardcoded user-visible copy — every string/image/link is an editable setting (see [references/editable-settings.md](references/editable-settings.md#granularity-doctrine-settings-first-static-blocks))
103
103
  - Array-setting rows keyed by **index**, never by a builder-editable field value
104
104
  - Media settings that may start empty (`src: ""`) gated with a conditional render or placeholder — never an unconditional `<img src={setting.src}>`
105
- - **Deploying through the MCP:** `errors: null` on a push is not proof — compare the push result's `sourceSha256` with `shasum -a 256` of the file you sent, every byte counted, trailing newline included (Softr stores exactly what it receives; the one-byte drift we once blamed on it was a chunked read on our side). Prove deployed == disk *before* editing the same way, with `vibe_coding_block_get_code` and `includeCode: false`, so a Studio-side change is never overwritten. Fetch the full `sourceCode` only when a digest is missing or the hashes differ. Protocol in [references/softr-mcp.md → Verifying a push](references/softr-mcp.md#verifying-a-push--the-deployed-source-is-the-only-proof)
105
+ - **Deploying through the MCP:** `errors: null` on a push is not proof — compare the push result's `sourceSha256` with `shasum -a 256` of the file you sent, every byte counted, trailing newline included (Softr stores exactly the text that reaches it; the one-byte drift we once blamed on it was a chunked read on our side). No `\uXXXX` escapes in pushed source: they arrive decoded to the characters and the hashes differ, so write the characters themselves (with a comment saying why) or build them from code points (`String.fromCharCode(0x300)`); lone surrogates are the exception ([why](references/softr-mcp.md#unicode-escapes-come-back-decoded)). Prove deployed == disk *before* editing the same way, with `vibe_coding_block_get_code` and `includeCode: false`, so a Studio-side change is never overwritten. Fetch the full `sourceCode` only when a digest is missing or the hashes differ. Protocol in [references/softr-mcp.md → Verifying a push](references/softr-mcp.md#verifying-a-push--the-deployed-source-is-the-only-proof)
106
106
  - **Then check it in a browser.** Once the push checks pass, check rendering and behaviour in a fresh preview with saves blocked, per [references/browser-checks.md](references/browser-checks.md)
107
107
 
108
108
  ## What to Clarify
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "softr-vibe-coding",
3
- "version": "2.15.0",
3
+ "version": "2.15.1",
4
4
  "description": "Claude Code skill for generating production-ready Softr Vibe Coding blocks (JSX). Installs into ~/.claude/skills/ and auto-updates on each Claude Code session.",
5
5
  "bin": {
6
6
  "softr-vibe-coding": "bin/cli.js"
@@ -280,7 +280,7 @@ reports neither field. With `includeCode: false`, `vibe_coding_block_get_code` r
280
280
  `sourceCode: null`. Verified live 2026-10-01: a deployed block's `sourceSha256` equalled the SHA-256
281
281
  of the exact source last pushed to it (taken from the push call), and the read came back at about
282
282
  1 KB for a 15 KB block. The same check showed that block's local mirror had picked up three comment
283
- edits since that push, which is exactly what step 1 below exists to catch. (The digest on push results is per Softr's release notes; no push of ours has shown it yet.)
283
+ edits since that push, which is exactly what step 1 below exists to catch. (The digest on push results was first known from Softr's release notes; our own pushes have returned it since at least 2026-10-08.)
284
284
  Right after that release our client's copy of the tool definition did not declare `includeCode`, so
285
285
  the argument went out as the string `"false"` and the server still honoured it. Whatever the loaded
286
286
  definition says, check that `sourceCode` came back `null`.
@@ -308,10 +308,12 @@ definition says, check that `sourceCode` came back `null`.
308
308
 
309
309
  Matching hashes prove what Softr stored, not how the block behaves: for that, check it in a fresh preview with saves blocked, per [browser-checks.md](browser-checks.md).
310
310
 
311
- **Hash the exact bytes, trailing newline included.** Softr stores exactly what it receives: across
311
+ **Hash the exact bytes, trailing newline included.** Softr stores exactly the text that reaches it: across
312
312
  112 push→fetch pairs between 2026-09-09 and 2026-09-30 the fetched
313
313
  `sourceCode` was byte- and MD5-identical to the text sent, including two pushes sent *without* a
314
- final newline and stored without one. The "deployed block is one byte shorter" we chased on
314
+ final newline and stored without one. (One thing does not reach Softr as written: a `\uXXXX`
315
+ escape in the source arrives decoded, see [below](#unicode-escapes-come-back-decoded).)
316
+ The "deployed block is one byte shorter" we chased on
315
317
  2026-09-09 was our own read: an agent that reads a large file in chunks can drop the final
316
318
  newline (or a blank line at a chunk boundary) before transmission. A comparison that normalises
317
319
  the trailing newline hides exactly that class of error — so do not normalise anything; a mismatch
@@ -356,6 +358,41 @@ from the docs):
356
358
  (`application_get`) with the version's `createdAt` (`vibe_coding_block_list_versions`): on
357
359
  2026-09-01 a block we believed staged went live with a publish 28 minutes after it was saved.
358
360
 
361
+ #### Unicode escapes come back decoded
362
+
363
+ Verified 2026-10-08 on a large dashboard block pushed in stages with `vibe_coding_block_update_code`
364
+ and `vibe_coding_block_update_code_search_replace`: exactly the calls whose text held a JavaScript
365
+ `\uXXXX` escape came back with a `sourceSha256` that differed from the hash of the planned text. The
366
+ block's accent-folding regex, `/[\u0300-\u036f]/g`, was stored with both escapes replaced by the
367
+ characters themselves, two combining marks sitting raw between the brackets. `\d`, `\D` and `\s` in
368
+ the same block arrived as written.
369
+
370
+ The decoding happens somewhere between the tool call and storage (the client's JSON handling of
371
+ the argument, or the server), not in Softr's compiler: the stored source text itself changed. It
372
+ looks like the [encoding trap](#which-edit-tool-full-replace-vs-targeted-search-replace) above,
373
+ reaching a backslash-u that was meant to stay in the source. The exception is a lone surrogate:
374
+ `\udc00-\udfff` in the same regexes has no character form, and those pushes stored it unchanged.
375
+ (The `\u{…}` form is untested; treat it the same.)
376
+
377
+ **So: no `\uXXXX` in pushed source, lone surrogates aside.** Find them before a push with
378
+ `grep -n '\\u[0-9a-fA-F]\{4\}' <file>`, then either:
379
+
380
+ - **Write the characters themselves**, with a comment saying why, since combining marks and other
381
+ invisible characters cannot be read in an editor. Spell "backslash-u" out in that comment so it
382
+ holds no escape either. Check first that the character means the same raw in that spot: a range
383
+ of combining marks in a character class does (the block's regexes, escaped and raw, matched the
384
+ same set across all 65,536 BMP code points), but written raw, a `/` ends a regex literal, a `]`
385
+ closes a class, a backslash or quote changes the token, and U+2028/U+2029 are line terminators a
386
+ regex literal may not contain.
387
+ - **Build it from code points** where a raw character is unwanted or unsafe:
388
+ `new RegExp("[" + String.fromCharCode(0x300) + "-" + String.fromCharCode(0x36f) + "]", "g")`,
389
+ created once at module scope. No backslash-u reaches the tool, so there is nothing to decode.
390
+ Strings likewise: `String.fromCharCode(0x2014)`, not `"\u2014"`.
391
+
392
+ If a push has already stored the decoded form, applying the same replacement to the mirror brings
393
+ the hashes back together; confirm the raw characters behave the same (first bullet) before calling
394
+ it done.
395
+
359
396
  ### The array-argument rejection, and why it is a security issue
360
397
 
361
398
  **Several workspace-server tools take an array argument, and a call that sends it as a JSON *string*