@polycode-projects/the-mechanical-code-talker 2.7.18 → 2.7.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polycode-projects/the-mechanical-code-talker",
3
- "version": "2.7.18",
3
+ "version": "2.7.20",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "The Mechanical Code Talker (tmct) — a tolerant, offline, $0 chat surface that guides you toward precision queries about a software repository. ELIZA/PARRY-style but domain-obsessed with code. No model calls; no codebase index of its own.",
@@ -544,7 +544,15 @@ async function runWorldCommand(cmd, { world, memoryDir, env, graph, cache }) {
544
544
  // the same way "what am I carrying" already reads inventory contents.
545
545
  // talk has no carried exception: NPCs are never portable.
546
546
  const carried = cmd.verb === "examine" && carriedByPlayer(state, object);
547
- if (!carried && visibleRoomOf(object, { rows, state }) !== here) {
547
+ // The room the player is standing in is never the SUBJECT of a placement
548
+ // fact (only ever the OBJECT other things are placed in), so
549
+ // visibleRoomOf(object) can never equal `here` for a room's own name —
550
+ // "examine the study" while standing in the study would otherwise
551
+ // decline "I don't see a study here", which reads as the room not
552
+ // existing rather than the true state (you're in it). Treat naming the
553
+ // current room itself as always present.
554
+ const isCurrentRoom = object === here;
555
+ if (!carried && !isCurrentRoom && visibleRoomOf(object, { rows, state }) !== here) {
548
556
  return answer(
549
557
  `I don't see a ${object} here.`,
550
558
  noteFor(`${cmd.verb} — ${object} isn't visible in the ${here}; declined, hidden things stay hidden`),
@@ -677,9 +677,20 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
677
677
  // later, as if it were still on the board. Drop it from `agents` (its own
678
678
  // goal is moot) and let the eating spider's line say what actually
679
679
  // happened instead of the now-false "co-located with fly-5 in the web".
680
+ // A spider can eat more than one fly in the same tick (several flies
681
+ // co-located with it on the same cell) — group by spider first, rather
682
+ // than overwriting the goal once per eaten fly, which silently credited
683
+ // only the LAST one and dropped every earlier fly from the summary line
684
+ // (the "Turn N — ..." event text already lists every eaten fly correctly;
685
+ // only this goal-line summary was collapsing them).
686
+ const eatenBySpider = new Map();
680
687
  for (const { fly, spider } of ecology.events.eaten) {
681
688
  delete agents[fly];
682
- if (agents[spider]) agents[spider].goal = `just ate ${fly} in the web.`;
689
+ if (!eatenBySpider.has(spider)) eatenBySpider.set(spider, []);
690
+ eatenBySpider.get(spider).push(fly);
691
+ }
692
+ for (const [spider, flyIds] of eatenBySpider) {
693
+ if (agents[spider]) agents[spider].goal = `just ate ${flyIds.join(" and ")} in the web.`;
683
694
  }
684
695
  for (const flyId of ecology.events.starved) delete agents[flyId];
685
696
  const writes = [...movementWrites, ...ecology.writes];