@maka/maka-cli 5.143.1 → 5.144.0

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.143.1",
3
+ "version": "5.144.0",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 3.x applications using React.",
@@ -3,6 +3,7 @@ import { spreadStationedNpcs } from './npc-spread.js';
3
3
  import { Logger } from '../utilities/logger.js';
4
4
  import { catalogAugmentations } from '../utilities/catalog.js';
5
5
  import { isOutdoorsPlace } from '../utilities/outdoors.js';
6
+ import { clampDeviceKind } from '../utilities/affordances.js';
6
7
  /**
7
8
  * CHUNKED scene generation (see SceneSeedGenerator.generate for the
8
9
  * pipeline that drives this module). One monolithic whole-scene completion
@@ -2176,10 +2177,11 @@ export function assembleScene(skeleton, details, playerName) {
2176
2177
  ? detail.guardsExit.toLowerCase()
2177
2178
  : undefined;
2178
2179
  const footprint = guardsExit && detail?.footprint && VALID_FOOTPRINTS.has(detail.footprint) ? detail.footprint : undefined;
2179
- // KIND CLAMPS, never throws. A device whose kind we cannot read is
2180
- // still a device, and rejecting a whole draft over one word is a
2181
- // worse outcome than a panel where the model meant something else.
2182
- const kind = device.kind === 'lock' || device.kind === 'ward' ? device.kind : 'panel';
2180
+ // KIND CLAMPS, never throws -- see clampDeviceKind's own doc for
2181
+ // why, and for the incident (a "tunnel grate" clamped to `panel`
2182
+ // and rendered hack-only) that put a name/description-aware
2183
+ // fallback here instead of a blind default to `panel`.
2184
+ const kind = clampDeviceKind(device.kind, device.name, detail?.description ?? device.concept);
2183
2185
  // A CODE ONLY BELONGS ON A PANEL -- enforced here as well as in the
2184
2186
  // prompt, because the prompt is advice and this is not. You cannot
2185
2187
  // talk at a mechanical lock.
@@ -4,6 +4,7 @@ import { AI } from '../../../../tools/ai/ai.class.js';
4
4
  import { Player } from '../models/player.js';
5
5
  import { Room } from '../models/room.js';
6
6
  import { Logger } from '../utilities/logger.js';
7
+ import { clampDeviceKind } from '../utilities/affordances.js';
7
8
  import { GenerationCapture } from '../utilities/generation-capture.js';
8
9
  import { fetchCanonContext } from '../utilities/canon-lore.js';
9
10
  import { SceneSynthesizer } from './scene-factory.js';
@@ -681,17 +682,23 @@ export class SceneSeedGenerator {
681
682
  * This used to throw on an unrecognised bypassType. A kind we cannot
682
683
  * read is still a device, and rejecting a whole draft -- 20-40s of
683
684
  * generation -- over one misspelled word is a worse outcome than a
684
- * panel where the model meant something else. assembleScene does the
685
- * same clamp; this one catches a seed that reached us another way.
685
+ * guess where the model meant something else. assembleScene does the
686
+ * same clamp (via the same clampDeviceKind helper, so the two cannot
687
+ * drift); this one catches a seed that reached us another way.
688
+ *
689
+ * THE GUESS IS NAME/DESCRIPTION-AWARE, not a blind "panel" (fixed
690
+ * 2026-09-08, backlog 2uNPnc4L6jHaYEtnN / tuku6Z5XTtmNemkzd): a
691
+ * "tunnel grate" clamped to `panel` rendered as a hack-only "sealed
692
+ * SYSTEM", which is backwards for a physical mechanism.
686
693
  */
687
694
  static clampDeviceKinds(candidate) {
688
- const allowed = new Set(['lock', 'panel', 'ward']);
689
695
  for (const room of candidate.rooms ?? []) {
690
696
  for (const device of room.devices ?? []) {
691
- if (device.kind && !allowed.has(device.kind)) {
692
- Logger.getInstance().write(`scene-seed-generator: device "${device.name}" had kind "${device.kind}"; clamped to "panel".`);
693
- device.kind = 'panel';
697
+ const clamped = clampDeviceKind(device.kind, device.name, device.description);
698
+ if (device.kind && device.kind !== clamped) {
699
+ Logger.getInstance().write(`scene-seed-generator: device "${device.name}" had kind "${device.kind}"; clamped to "${clamped}".`);
694
700
  }
701
+ device.kind = clamped;
695
702
  }
696
703
  }
697
704
  }
@@ -33,4 +33,44 @@ export const AFFORDANCE_FLAVOR = {
33
33
  export function verbsFor(kind) {
34
34
  return AFFORDANCE_FLAVOR[kind]?.verbs ?? [];
35
35
  }
36
+ /** WARD-SOUNDING NAMES, checked first: a magical barrier is rare and
37
+ * specific enough here that missing one costs less than a false
38
+ * positive stealing a genuine lock from `lock`. */
39
+ const WARD_HINTS = ['ward', 'seal', 'rune', 'glyph', 'weave', 'astral', 'spell', 'enchant', 'mystic'];
40
+ /** PHYSICAL-MECHANISM NAMES, checked second -- the actual fix
41
+ * (backlog 2uNPnc4L6jHaYEtnN / tuku6Z5XTtmNemkzd, 2026-09-08). A
42
+ * device that reads as a mechanism now falls back to `lock`
43
+ * (pick/breach) instead of `panel` (hack): defaulting a "tunnel
44
+ * grate" to panel had the game insist a metal grate was "a sealed
45
+ * SYSTEM", which is exactly backwards for a physical object. */
46
+ const LOCK_HINTS = [
47
+ 'grate', 'gate', 'hatch', 'grille', 'grill', 'padlock', 'deadbolt',
48
+ 'bolt', 'latch', 'mechanism', 'portcullis', 'chain', 'valve', 'hinge',
49
+ 'tumbler', 'maglock', 'lock', 'vault', 'safe', 'cage', 'shutter',
50
+ 'manhole', 'trapdoor', 'hasp',
51
+ ];
52
+ /**
53
+ * THE ONE KIND CLAMP, shared by both scene-chunks.ts's assembleScene and
54
+ * scene-seed-generator.ts's clampDeviceKinds so the fallback cannot
55
+ * drift between them the way a bare "default to panel" already had.
56
+ *
57
+ * NEVER THROWS -- deliberately, and for the reason both call sites'
58
+ * own comments already gave before this existed: a kind the model got
59
+ * wrong is still a device, and rejecting a whole generated scene over
60
+ * one word costs the player a 20-40s regeneration for a mistake this
61
+ * heuristic can approximate better. This only changes WHAT the
62
+ * fallback guesses when the raw kind isn't one of the three real
63
+ * ones -- from a blind `panel` to a look at the device's own name and
64
+ * description first.
65
+ */
66
+ export function clampDeviceKind(rawKind, name, description) {
67
+ if (rawKind === 'lock' || rawKind === 'panel' || rawKind === 'ward')
68
+ return rawKind;
69
+ const text = `${name} ${description ?? ''}`.toLowerCase();
70
+ if (WARD_HINTS.some(hint => text.includes(hint)))
71
+ return 'ward';
72
+ if (LOCK_HINTS.some(hint => text.includes(hint)))
73
+ return 'lock';
74
+ return 'panel';
75
+ }
36
76
  //# sourceMappingURL=affordances.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.143.1",
3
+ "version": "5.144.0",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 3.x applications using React.",