castle-web-cli 0.4.168 → 0.4.169

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.
@@ -102,6 +102,7 @@ const PLAN_RULES = `Reading the user, fresh from EACH message -- the same user h
102
102
  - CLEAR SPEC (mechanics named, a reference given, "like X but Y"): the gaps are closed. Build what they said; never probe taste they already expressed.
103
103
  - FRAGMENT ("something with ants", "a cozy game about tea?"): gaps they cannot name yet. Draw their taste out on the few dimensions that decide whether this feels like THEIRS -- while building, never instead of building.
104
104
  - A fragment is not only a whole-game ask. "how about some obstacles?", "make it feel brighter" name a slice and leave its taste open: build the part they named, and probe the ONE dimension you were about to settle for them. Deciding it silently is how a deck becomes yours instead of theirs.
105
+ - A build turn that was going to carry NO question at all is where a feel question belongs, and on a deck already underway most turns are that: they name a change, you make it, nothing is asked, and the feel of it was yours by default. If what you are about to build has a feel they have not named -- how hard it should be, how fast it should move, how it should land -- ask that one thing in the same reply as the work. This never replaces a question you were already going to ask: still one at a time, and if you already had one, keep it.
105
106
  - DELEGATION ("surprise me", "you decide"): the open calls are yours. Decide; probe nothing.
106
107
  - Praise, a question, plain conversation: answer it and do NOTHING else -- no tasks, no probe, no chips, no plan fence. Never manufacture work to seem engaged.
107
108
 
@@ -339,11 +340,15 @@ function takeSource(text) {
339
340
  }
340
341
  const QUOTE_GLYPHS = /["'‘’‚‛′“”„‟″]/g;
341
342
  const DASH_GLYPHS = /[‐-―−]/g;
343
+ const BACKSLASH_ESCAPE = /\\(?=[^\\])/g;
344
+ const DASH_RUN = /-{2,}/g;
342
345
  const EDGE_PUNCT = /^['.,;:!?()[\]{}\s-]+|['.,;:!?()[\]{}\s-]+$/g;
343
346
  function normalizeQuote(text) {
344
347
  return text
348
+ .replace(BACKSLASH_ESCAPE, "")
345
349
  .replace(QUOTE_GLYPHS, "'")
346
350
  .replace(DASH_GLYPHS, "-")
351
+ .replace(DASH_RUN, "-")
347
352
  .toLowerCase()
348
353
  .replace(/\s+/g, " ")
349
354
  .replace(EDGE_PUNCT, "");
@@ -354,6 +359,18 @@ function normalizeQuote(text) {
354
359
  // whitespace, and failing a citation over a smart apostrophe would teach the
355
360
  // router to stop quoting. The words and their order have to match exactly.
356
361
  //
362
+ // Two transcription habits cost real citations before they were normalized
363
+ // here, both measured 26-09-06 over every stored t1 run (17 of 17 routed taste
364
+ // ops recovered, 0 genuine fabrications among them):
365
+ // - The router writes `--` because the whole prompt is written in `--`, where
366
+ // the user typed an em dash. Mapping one glyph to one hyphen is not enough;
367
+ // the RUN has to collapse.
368
+ // - It escapes inner quotes -- ("Rename the game to \"Cube-Tac\"") -- when the
369
+ // words it is quoting contain quotes, which is what naming a thing is. Both
370
+ // of Eric's dogfood decks lost their game's NAME to this: the decision was
371
+ // filed as a proposal and asked back at him a few turns later.
372
+ // Neither can make a false citation match: the words still have to be there.
373
+ //
357
374
  // Exported so the eval harness checks a fence with the byte-identical function
358
375
  // the runtime applies it with.
359
376
  export function quoteMatches(fragment, sourceText) {
package/dist/save-deck.js CHANGED
@@ -369,7 +369,17 @@ export async function saveDeck(dir, opts = {}) {
369
369
  cardId: result.cardId,
370
370
  title,
371
371
  };
372
- fs.writeFileSync(path.join(projectDir, 'castle.json'), JSON.stringify(newCastleJson, null, 2) + '\n', 'utf-8');
372
+ // Only write when the bytes actually move. An identical rewrite still bumps
373
+ // mtime, which the serve's watcher reports as a change like any other -- so
374
+ // a push that changed nothing here would flip the Play panel to "Changes
375
+ // available" and put castle.json in the next saved version's diff.
376
+ const castleJsonPath = path.join(projectDir, 'castle.json');
377
+ const nextCastleJson = JSON.stringify(newCastleJson, null, 2) + '\n';
378
+ const onDisk = fs.existsSync(castleJsonPath)
379
+ ? fs.readFileSync(castleJsonPath, 'utf-8')
380
+ : null;
381
+ if (onDisk !== nextCastleJson)
382
+ fs.writeFileSync(castleJsonPath, nextCastleJson, 'utf-8');
373
383
  if (isNew) {
374
384
  console.log(`Created deck "${title}" (${result.deckId}). Saved castle.json.`);
375
385
  }