Make the history command more readable #448

Merged
LunarTides merged 7 commits from history into main 2026-01-20 14:19:17 +00:00
LunarTides commented 2026-01-05 09:57:52 +00:00 (Migrated from github.com)

Closes #445

Summary by CodeRabbit

  • New Features

    • Added configurable readableHistory hooks for customizable, human-readable event history entries.
    • Player entities now expose a readable representation for clearer display.
  • Refactor

    • Unified and simplified history rendering, hide/reveal behavior, and history entry formatting for consistent output.
  • Bug Fixes

    • History initialization no longer auto-inserts a default game-loop entry, preventing duplicate initial entries.
  • Chores

    • Types expanded to support new history formatting hooks.

✏️ Tip: You can customize this high-level summary in your review settings.

Closes #445 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added configurable readableHistory hooks for customizable, human-readable event history entries. * Player entities now expose a readable representation for clearer display. * **Refactor** * Unified and simplified history rendering, hide/reveal behavior, and history entry formatting for consistent output. * **Bug Fixes** * History initialization no longer auto-inserts a default game-loop entry, preventing duplicate initial entries. * **Chores** * Types expanded to support new history formatting hooks. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
coderabbitai[bot] commented 2026-01-05 09:57:57 +00:00 (Migrated from github.com)

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds a configurable per-event readableHistory mapping to produce human-readable history entries, removes Event.GameLoop (switching tick emission to Event.Dummy), updates history initialization and rendering logic, adjusts tests and interaction prompts, and adds Player.readable() plus a minor comment in Card.readable().

Changes

Cohort / File(s) Summary
Tests
cards/Debug/inf-mana-47a7e94b.ts, cards/Examples/4/5-tick-hook.ts
Replaced tick calls from Event.GameLoop with Event.Dummy and undefined payloads (tick trigger/source changed).
Config
config.ts
Added config.advanced.readableHistory with per-event async handlers; expanded public imports to include Alignment and TargetType.
Types
src/types/event.ts, src/types/other.ts
Removed Event.GameLoop enum member and its EventValue mapping; added EventValue import and typed GameConfig.advanced.readableHistory.
History & Commands
src/event.ts, src/commands.ts
Initialize per-turn history as an empty array; introduced handle(value, hide) flow; integrated readableHistory callbacks when rendering history; adjusted hide/reveal checks and history formatting.
Runtime / Interaction
src/functions/interact.ts
Switched tick emission from Event.GameLoop to Event.Dummy; unified input wrapper signature; changed prompt call/spacing in UI path.
Public API & Docs
src/player.ts, src/card.ts
Added Player.readable() method; inserted a TODO comment in Card.readable().

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client as Client
  participant Game as Game
  participant EventSystem as EventSystem
  participant Commands as Commands
  participant Config as Config

  Client->>Game: user action or tick trigger
  Game->>EventSystem: emit Event.Dummy (tick)
  EventSystem->>Game: update state
  Game->>EventSystem: addHistory(turn, eventKey, value)
  Client->>Commands: request history rendering
  Commands->>Config: lookup advanced.readableHistory[eventKey]?
  alt handler exists
    Commands->>Config: call handler(player, value, handle)
    Config-->>Commands: Promise<string> formattedEntry
  else no handler
    Commands->>Commands: call handle(value, hide) -> string
  end
  Commands->>Client: rendered history lines

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • LunarTides/Hearthstone.js#448 — adds/updates config.advanced.readableHistory handlers and history rendering changes; strong overlap with this PR's history formatting work.
  • LunarTides/Hearthstone.js#438 — modifies gameloop/tick emission and interaction flow similar to src/functions/interact.ts and test tick changes.

Suggested labels

breaking change

Poem

🐰
I hopped through history's new door,
Events now whisper words, not lore,
GameLoop hushed, Dummy gives a tap,
Players read lines on tidy map,
A carrot-cheer for clearer clap!

🚥 Pre-merge checks | 5
Passed checks (5 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The PR title directly aligns with the primary objective of making history command output more readable through human-readable event messages.
Linked Issues check Passed The PR implements the core requirement of #445: adds readableHistory config mapping for human-readable event messages and integrates it into the history command.
Out of Scope Changes check Passed Event system changes (removing GameLoop, adding Dummy event) and Player.readable() method are infrastructure changes supporting the readableHistory feature, which is within scope.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

<!-- This is an auto-generated comment: summarize by coderabbit.ai --> <!-- This is an auto-generated comment: failure by coderabbit.ai --> > [!CAUTION] > ## Review failed > > The pull request is closed. <!-- end of auto-generated comment: failure by coderabbit.ai --> <!-- walkthrough_start --> <details> <summary>📝 Walkthrough</summary> ## Walkthrough Adds a configurable per-event `readableHistory` mapping to produce human-readable history entries, removes `Event.GameLoop` (switching tick emission to `Event.Dummy`), updates history initialization and rendering logic, adjusts tests and interaction prompts, and adds `Player.readable()` plus a minor comment in `Card.readable()`. ## Changes | Cohort / File(s) | Summary | |---|---| | **Tests** <br> `cards/Debug/inf-mana-47a7e94b.ts`, `cards/Examples/4/5-tick-hook.ts` | Replaced tick calls from `Event.GameLoop` with `Event.Dummy` and `undefined` payloads (tick trigger/source changed). | | **Config** <br> `config.ts` | Added `config.advanced.readableHistory` with per-event async handlers; expanded public imports to include `Alignment` and `TargetType`. | | **Types** <br> `src/types/event.ts`, `src/types/other.ts` | Removed `Event.GameLoop` enum member and its `EventValue` mapping; added `EventValue` import and typed `GameConfig.advanced.readableHistory`. | | **History & Commands** <br> `src/event.ts`, `src/commands.ts` | Initialize per-turn history as an empty array; introduced `handle(value, hide)` flow; integrated `readableHistory` callbacks when rendering history; adjusted hide/reveal checks and history formatting. | | **Runtime / Interaction** <br> `src/functions/interact.ts` | Switched tick emission from `Event.GameLoop` to `Event.Dummy`; unified input wrapper signature; changed prompt call/spacing in UI path. | | **Public API & Docs** <br> `src/player.ts`, `src/card.ts` | Added `Player.readable()` method; inserted a TODO comment in `Card.readable()`. | ## Sequence Diagram(s) ```mermaid sequenceDiagram autonumber participant Client as Client participant Game as Game participant EventSystem as EventSystem participant Commands as Commands participant Config as Config Client->>Game: user action or tick trigger Game->>EventSystem: emit Event.Dummy (tick) EventSystem->>Game: update state Game->>EventSystem: addHistory(turn, eventKey, value) Client->>Commands: request history rendering Commands->>Config: lookup advanced.readableHistory[eventKey]? alt handler exists Commands->>Config: call handler(player, value, handle) Config-->>Commands: Promise<string> formattedEntry else no handler Commands->>Commands: call handle(value, hide) -> string end Commands->>Client: rendered history lines ``` ## Estimated code review effort 🎯 4 (Complex) | ⏱️ ~45 minutes ## Possibly related PRs - LunarTides/Hearthstone.js#448 — adds/updates `config.advanced.readableHistory` handlers and history rendering changes; strong overlap with this PR's history formatting work. - LunarTides/Hearthstone.js#438 — modifies gameloop/tick emission and interaction flow similar to `src/functions/interact.ts` and test tick changes. ## Suggested labels `breaking change` ## Poem > 🐰 > I hopped through history's new door, > Events now whisper words, not lore, > GameLoop hushed, Dummy gives a tap, > Players read lines on tidy map, > A carrot-cheer for clearer clap! </details> <!-- walkthrough_end --> <!-- pre_merge_checks_walkthrough_start --> <details> <summary>🚥 Pre-merge checks | ✅ 5</summary> <details> <summary>✅ Passed checks (5 passed)</summary> | Check name | Status | Explanation | | :------------------------: | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. | | Title check | ✅ Passed | The PR title directly aligns with the primary objective of making history command output more readable through human-readable event messages. | | Linked Issues check | ✅ Passed | The PR implements the core requirement of `#445`: adds readableHistory config mapping for human-readable event messages and integrates it into the history command. | | Out of Scope Changes check | ✅ Passed | Event system changes (removing GameLoop, adding Dummy event) and Player.readable() method are infrastructure changes supporting the readableHistory feature, which is within scope. | | Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. | </details> <sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub> </details> <!-- pre_merge_checks_walkthrough_end --> <!-- finishing_touch_checkbox_start --> <details> <summary>✨ Finishing touches</summary> - [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate docstrings </details> <!-- finishing_touch_checkbox_end --> <!-- tips_start --> --- Thanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=LunarTides/Hearthstone.js&utm_content=448)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. <details> <summary>❤️ Share</summary> - [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) - [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) - [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) - [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code) </details> <sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub> <!-- tips_end --> <!-- internal state start --> <!-- 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 --> <!-- internal state end -->
coderabbitai[bot] (Migrated from github.com) reviewed 2026-01-20 13:50:40 +00:00
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@config.ts`:
- Around line 465-466: In the Event.SpellDealsDamage template string the closing
bold tag is malformed; update the arrow function handler for
Event.SpellDealsDamage (parameters plr, [target, amount], handle) so the
returned string uses a proper closing tag — change `<b>${amount}<b>` to
`<b>${amount}</b>` in the template returned by that function to correctly close
the HTML bold tag.
- Around line 390-391: The HealthRestored message is misleading because
Event.HealthRestored handlers receive the new total health, not the delta;
update the template string in the Event.HealthRestored handler (the arrow for
[Event.HealthRestored]) to say "restored to <b>${await handle(value)}</b>
health" (or otherwise compute delta if you prefer) so the text matches the value
passed by the event.
🧹 Nitpick comments (2)
src/card.ts (1)

2074-2074: Noted: TODO for debug info placement.

This TODO comment is clear. Would you like me to open an issue to track this task, or would you prefer to address it in a follow-up PR?

src/commands.ts (1)

522-530: Type assertion workaround is acceptable but consider documenting the reason.

The as never cast on line 525 is a common workaround for TypeScript's limitation when the event key type cannot be narrowed within a loop. The TODO comment acknowledges this, but consider expanding it to explain why it's needed (TypeScript can't correlate the key variable with the generic E in readableHistory[key]).

The fallback logic correctly uses the processed newValue array when no custom handler exists.

📝 Suggested documentation improvement
 let entry = await game.config.advanced.readableHistory[key]?.(
   player,
-  // TODO: Use of never.
+  // TypeScript cannot narrow `key` to correlate with EventValue<E> here,
+  // so we cast to `never` to satisfy the generic constraint.
   value as never,
   (value: unknown, hide?: boolean) => handle(value, hide ?? shouldHide),
 );
**Actionable comments posted: 2** <details> <summary>🤖 Fix all issues with AI agents</summary> ``` In `@config.ts`: - Around line 465-466: In the Event.SpellDealsDamage template string the closing bold tag is malformed; update the arrow function handler for Event.SpellDealsDamage (parameters plr, [target, amount], handle) so the returned string uses a proper closing tag — change `<b>${amount}<b>` to `<b>${amount}</b>` in the template returned by that function to correctly close the HTML bold tag. - Around line 390-391: The HealthRestored message is misleading because Event.HealthRestored handlers receive the new total health, not the delta; update the template string in the Event.HealthRestored handler (the arrow for [Event.HealthRestored]) to say "restored to <b>${await handle(value)}</b> health" (or otherwise compute delta if you prefer) so the text matches the value passed by the event. ``` </details> <details> <summary>🧹 Nitpick comments (2)</summary><blockquote> <details> <summary>src/card.ts (1)</summary><blockquote> `2074-2074`: **Noted: TODO for debug info placement.** This TODO comment is clear. Would you like me to open an issue to track this task, or would you prefer to address it in a follow-up PR? </blockquote></details> <details> <summary>src/commands.ts (1)</summary><blockquote> `522-530`: **Type assertion workaround is acceptable but consider documenting the reason.** The `as never` cast on line 525 is a common workaround for TypeScript's limitation when the event key type cannot be narrowed within a loop. The TODO comment acknowledges this, but consider expanding it to explain *why* it's needed (TypeScript can't correlate the `key` variable with the generic `E` in `readableHistory[key]`). The fallback logic correctly uses the processed `newValue` array when no custom handler exists. <details> <summary>📝 Suggested documentation improvement</summary> ```diff let entry = await game.config.advanced.readableHistory[key]?.( player, - // TODO: Use of never. + // TypeScript cannot narrow `key` to correlate with EventValue<E> here, + // so we cast to `never` to satisfy the generic constraint. value as never, (value: unknown, hide?: boolean) => handle(value, hide ?? shouldHide), ); ``` </details> </blockquote></details> </blockquote></details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
@ -365,6 +371,127 @@ export const config: GameConfig = {
Event.AddCardToHand,
coderabbitai[bot] (Migrated from github.com) commented 2026-01-20 13:50:39 +00:00

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all Event.HealthRestored usages
rg -n 'Event\.HealthRestored' -A 2 -B 2 --type=ts

Repository: LunarTides/Hearthstone.js

Length of output: 1147


🏁 Script executed:

#!/bin/bash
# Also check the event type definition
rg -n 'HealthRestored' src/types/event.ts -A 3 -B 3

Repository: LunarTides/Hearthstone.js

Length of output: 505


🏁 Script executed:

#!/bin/bash
# Search for where health restore events are broadcast to see what value is passed
rg -n 'broadcast.*[Hh]ealth' --type=ts -A 1 -B 1 | head -100

Repository: LunarTides/Hearthstone.js

Length of output: 235


Fix misleading health restoration message text.

The message says "restored <b>${value}</b> health", which implies value is the amount restored (delta). However, examining the broadcasts in src/card.ts (lines 914, 919), the HealthRestored event passes the resulting health total (this.maxHealth or this.health), not the delta. The type comment in src/types/event.ts (lines 143-146) correctly reflects this as "The amount of health after restore."

Change the message to "restored to <b>${value}</b> health" or calculate and pass the delta as the event value instead.

🤖 Prompt for AI Agents
In `@config.ts` around lines 390 - 391, The HealthRestored message is misleading
because Event.HealthRestored handlers receive the new total health, not the
delta; update the template string in the Event.HealthRestored handler (the arrow
for [Event.HealthRestored]) to say "restored to <b>${await handle(value)}</b>
health" (or otherwise compute delta if you prefer) so the text matches the value
passed by the event.

Addressed in commit f9712c9

_⚠️ Potential issue_ | _🟡 Minor_ <details> <summary>🧩 Analysis chain</summary> 🏁 Script executed: ```shell #!/bin/bash # Find all Event.HealthRestored usages rg -n 'Event\.HealthRestored' -A 2 -B 2 --type=ts ``` Repository: LunarTides/Hearthstone.js Length of output: 1147 --- 🏁 Script executed: ```shell #!/bin/bash # Also check the event type definition rg -n 'HealthRestored' src/types/event.ts -A 3 -B 3 ``` Repository: LunarTides/Hearthstone.js Length of output: 505 --- 🏁 Script executed: ```shell #!/bin/bash # Search for where health restore events are broadcast to see what value is passed rg -n 'broadcast.*[Hh]ealth' --type=ts -A 1 -B 1 | head -100 ``` Repository: LunarTides/Hearthstone.js Length of output: 235 --- </details> **Fix misleading health restoration message text.** The message says `"restored <b>${value}</b> health"`, which implies `value` is the amount restored (delta). However, examining the broadcasts in `src/card.ts` (lines 914, 919), the `HealthRestored` event passes the resulting health total (`this.maxHealth` or `this.health`), not the delta. The type comment in `src/types/event.ts` (lines 143-146) correctly reflects this as "The amount of health after restore." Change the message to `"restored to <b>${value}</b> health"` or calculate and pass the delta as the event value instead. <details> <summary>🤖 Prompt for AI Agents</summary> ``` In `@config.ts` around lines 390 - 391, The HealthRestored message is misleading because Event.HealthRestored handlers receive the new total health, not the delta; update the template string in the Event.HealthRestored handler (the arrow for [Event.HealthRestored]) to say "restored to <b>${await handle(value)}</b> health" (or otherwise compute delta if you prefer) so the text matches the value passed by the event. ``` </details> <!-- fingerprinting:phantom:medusa:ocelot --> <!-- This is an auto-generated comment by CodeRabbit --> ✅ Addressed in commit f9712c9
coderabbitai[bot] (Migrated from github.com) commented 2026-01-20 13:50:39 +00:00

⚠️ Potential issue | 🟡 Minor

Unclosed bold tag.

The closing tag is missing a forward slash: <b>${amount}<b> should be <b>${amount}</b>.

🔧 Proposed fix
 [Event.SpellDealsDamage]: async (plr, [target, amount], handle) =>
-  `${await handle(target)} dealt <b>${amount}<b> damage`,
+  `${await handle(target)} dealt <b>${amount}</b> damage`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

			[Event.SpellDealsDamage]: async (plr, [target, amount], handle) =>
				`${await handle(target)} dealt <b>${amount}</b> damage`,
🤖 Prompt for AI Agents
In `@config.ts` around lines 465 - 466, In the Event.SpellDealsDamage template
string the closing bold tag is malformed; update the arrow function handler for
Event.SpellDealsDamage (parameters plr, [target, amount], handle) so the
returned string uses a proper closing tag — change `<b>${amount}<b>` to
`<b>${amount}</b>` in the template returned by that function to correctly close
the HTML bold tag.

Addressed in commit f9712c9

_⚠️ Potential issue_ | _🟡 Minor_ **Unclosed bold tag.** The closing tag is missing a forward slash: `<b>${amount}<b>` should be `<b>${amount}</b>`. <details> <summary>🔧 Proposed fix</summary> ```diff [Event.SpellDealsDamage]: async (plr, [target, amount], handle) => - `${await handle(target)} dealt <b>${amount}<b> damage`, + `${await handle(target)} dealt <b>${amount}</b> damage`, ``` </details> <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. ```suggestion [Event.SpellDealsDamage]: async (plr, [target, amount], handle) => `${await handle(target)} dealt <b>${amount}</b> damage`, ``` </details> <!-- suggestion_end --> <details> <summary>🤖 Prompt for AI Agents</summary> ``` In `@config.ts` around lines 465 - 466, In the Event.SpellDealsDamage template string the closing bold tag is malformed; update the arrow function handler for Event.SpellDealsDamage (parameters plr, [target, amount], handle) so the returned string uses a proper closing tag — change `<b>${amount}<b>` to `<b>${amount}</b>` in the template returned by that function to correctly close the HTML bold tag. ``` </details> <!-- fingerprinting:phantom:medusa:ocelot --> <!-- This is an auto-generated comment by CodeRabbit --> ✅ Addressed in commit f9712c9
coderabbitai[bot] (Migrated from github.com) reviewed 2026-01-20 14:05:12 +00:00
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/commands.ts (1)

399-436: Reveal detection misses cards embedded in tuple event values.

Events like RevealCard, DamageCard, etc. store cards inside arrays, but the reveal scan only checks direct Card values. That can leave previously revealed cards shown as Hidden.

🧩 Fix by scanning arrays for matching cards
-// If it is not a card
-if (!(newValue instanceof Card)) {
-	continue;
-}
-
-if (value.uuid !== newValue.uuid) {
-	continue;
-}
-
-// The card has been revealed.
-revealed = true;
+const matches =
+	newValue instanceof Card
+		? newValue.uuid === value.uuid
+		: Array.isArray(newValue)
+			? newValue.some(
+					(item) => item instanceof Card && item.uuid === value.uuid,
+				)
+			: false;
+
+if (!matches) {
+	continue;
+}
+
+// The card has been revealed.
+revealed = true;
🤖 Fix all issues with AI agents
In `@config.ts`:
- Around line 375-494: The TargetSelectionStarts readableHistory entry uses
truthy checks for flags.targetType and flags.alignment which will treat valid
enum values 0 as missing; update the conditional expressions in the
TargetSelectionStarts handler to use explicit !== undefined checks (e.g.,
flags.targetType !== undefined and flags.alignment !== undefined) when deciding
whether to render TargetType[flags.targetType] or Alignment[flags.alignment] so
the fallback "All" is only used for undefined values; keep the rest of the
handler (plr, prompt, host, handle usage) unchanged.
🧹 Nitpick comments (1)
src/commands.ts (1)

377-384: Prefer Player.readable() for consistency.

This aligns history formatting with Card.readable() and future-proofs player formatting.

♻️ Suggested tweak
-if (value instanceof Player) {
-	return value.getName();
-}
+if (value instanceof Player) {
+	return await value.readable();
+}
**Actionable comments posted: 1** > [!CAUTION] > Some comments are outside the diff and can’t be posted inline due to platform limitations. > > > > <details> > <summary>⚠️ Outside diff range comments (1)</summary><blockquote> > > <details> > <summary>src/commands.ts (1)</summary><blockquote> > > `399-436`: **Reveal detection misses cards embedded in tuple event values.** > > Events like `RevealCard`, `DamageCard`, etc. store cards inside arrays, but the reveal scan only checks direct `Card` values. That can leave previously revealed cards shown as `Hidden`. > > > <details> > <summary>🧩 Fix by scanning arrays for matching cards</summary> > > ```diff > -// If it is not a card > -if (!(newValue instanceof Card)) { > - continue; > -} > - > -if (value.uuid !== newValue.uuid) { > - continue; > -} > - > -// The card has been revealed. > -revealed = true; > +const matches = > + newValue instanceof Card > + ? newValue.uuid === value.uuid > + : Array.isArray(newValue) > + ? newValue.some( > + (item) => item instanceof Card && item.uuid === value.uuid, > + ) > + : false; > + > +if (!matches) { > + continue; > +} > + > +// The card has been revealed. > +revealed = true; > ``` > </details> > > </blockquote></details> > > </blockquote></details> <details> <summary>🤖 Fix all issues with AI agents</summary> ``` In `@config.ts`: - Around line 375-494: The TargetSelectionStarts readableHistory entry uses truthy checks for flags.targetType and flags.alignment which will treat valid enum values 0 as missing; update the conditional expressions in the TargetSelectionStarts handler to use explicit !== undefined checks (e.g., flags.targetType !== undefined and flags.alignment !== undefined) when deciding whether to render TargetType[flags.targetType] or Alignment[flags.alignment] so the fallback "All" is only used for undefined values; keep the rest of the handler (plr, prompt, host, handle usage) unchanged. ``` </details> <details> <summary>🧹 Nitpick comments (1)</summary><blockquote> <details> <summary>src/commands.ts (1)</summary><blockquote> `377-384`: **Prefer `Player.readable()` for consistency.** This aligns history formatting with `Card.readable()` and future-proofs player formatting. <details> <summary>♻️ Suggested tweak</summary> ```diff -if (value instanceof Player) { - return value.getName(); -} +if (value instanceof Player) { + return await value.readable(); +} ``` </details> </blockquote></details> </blockquote></details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
coderabbitai[bot] (Migrated from github.com) commented 2026-01-20 14:05:11 +00:00

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "enum Alignment|enum TargetType" --type=ts

Repository: LunarTides/Hearthstone.js

Length of output: 161


🏁 Script executed:

sed -n '75,95p' src/types/other.ts

Repository: LunarTides/Hearthstone.js

Length of output: 371


🏁 Script executed:

sed -n '437,447p' config.ts

Repository: LunarTides/Hearthstone.js

Length of output: 467


🏁 Script executed:

rg -n "Event.TargetSelectionStarts" config.ts -A 5

Repository: LunarTides/Hearthstone.js

Length of output: 562


Replace enum lookups with explicit !== undefined checks in TargetSelectionStarts.

The current truthiness checks will fail when flags.targetType or flags.alignment is 0, which are valid enum values (TargetType.Player and Alignment.Friendly respectively). This causes the fallback "All" to display incorrectly.

Fix: Use explicit undefined checks
[Event.TargetSelectionStarts]: async (
	plr,
	[prompt, host, flags],
	handle,
) =>
	`${await handle(plr)} started selecting a target. [Prompt: "${prompt}", Host: ${host ? await handle(host) : "Game"}, Type: ${flags.targetType !== undefined ? TargetType[flags.targetType] : "All"}, Alignment: ${flags.alignment !== undefined ? Alignment[flags.alignment] : "All"}]`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

		/**
		 * For each event here, it will show the return value of the function in the history command.
		 *
		 * The `handle` function should be called when dealing with the value. Don't change the `hide` parameter unless you have a good reason to.
		 */
		readableHistory: {
			[Event.FatalDamage]: async (plr, value, handle) =>
				`${await handle(plr)} was dealt fatal damage`,

			[Event.EndTurn]: async (plr, value, handle) =>
				`${await handle(plr)} ended their turn`,

			[Event.StartTurn]: async (plr, value, handle) =>
				`${await handle(plr)} started their turn`,

			[Event.HealthRestored]: async (plr, value, handle) =>
				`${await handle(plr)} restored to <b>${await handle(value)}</b> health`,

			[Event.UnspentMana]: async (plr, value, handle) =>
				`${await handle(plr)} ended their turn with <b>${await handle(value)}</b> unspent mana`,

			[Event.GainOverload]: async (plr, value, handle) =>
				`${await handle(plr)} gained <b>${await handle(value)}</b> overload`,

			[Event.GainHeroAttack]: async (plr, value, handle) =>
				`${await handle(plr)} gained <b>${await handle(value)}</b> attack`,

			[Event.TakeDamage]: async (plr, value, handle) =>
				`${await handle(plr)} took <b>${await handle(value)}</b> damage`,

			[Event.PlayCard]: async (plr, value, handle) =>
				`${await handle(plr)} played ${await handle(value)}`,

			[Event.PlayCardUnsafe]: async (plr, value, handle) =>
				`${await handle(plr)} is trying to play a ${await handle(value)}`,

			[Event.SummonCard]: async (plr, value, handle) =>
				`${await handle(plr)} summoned a ${await handle(value)}`,

			[Event.DestroyCard]: async (plr, value, handle) =>
				`${await handle(value)} was destroyed`,

			[Event.DamageCard]: async (plr, [card, amount], handle) =>
				`${await handle(card)} was dealt <b>${await handle(amount)}</b> damage`,

			[Event.SilenceCard]: async (plr, value, handle) =>
				`${await handle(value)} was silenced`,

			[Event.DiscardCard]: async (plr, value, handle) =>
				`${await handle(value)} was discarded`,

			[Event.CancelCard]: async (plr, [card, ability], handle) =>
				`${await handle(card)}'s <b>${await handle(ability)}</b> was cancelled`,

			[Event.TradeCard]: async (plr, value, handle) =>
				`${await handle(value)} was traded`,

			[Event.ForgeCard]: async (plr, value, handle) =>
				`${await handle(value)} was forged`,

			[Event.FreezeCard]: async (plr, value, handle) =>
				`${await handle(value)} was frozen`,

			[Event.CreateCard]: async (plr, value, handle) =>
				`${await handle(value)} was created`,

			[Event.RevealCard]: async (plr, [card, reason], handle) =>
				`${await handle(card)} was revealed due to ${reason}`,

			[Event.BurnCard]: async (plr, value, handle) =>
				`${await handle(value)} was burned due to a lack of hand space`,

			[Event.Titan]: async (plr, [titan, ability], handle) =>
				`${await handle(titan)}'s titan ability (${await handle(ability)}) was triggered`,

			[Event.AddCardToDeck]: async (plr, value, handle) =>
				`${await handle(value)} was added to ${await handle(plr)}'s deck`,

			[Event.AddCardToHand]: async (plr, value, handle) =>
				`${await handle(value)} was added to ${await handle(plr)}'s hand`,

			[Event.DrawCard]: async (plr, value, handle) =>
				`${await handle(plr)} drew ${await handle(value)}`,

			[Event.ChangeLocation]: async (plr, [card, location], handle) =>
				`${await handle(card)}'s location was changed to <b>${await handle(location)}</b>`,

			[Event.ChangeHero]: async (plr, [oldHero, newHero], handle) =>
				`${await handle(plr)}'s hero has become ${await handle(newHero)}`,

			[Event.SpellDealsDamage]: async (plr, [target, amount], handle) =>
				`${await handle(target)} dealt <b>${amount}</b> damage`,

			[Event.Attack]: async (plr, [attacker, target, flags], handle) =>
				`${await handle(attacker)} attacked ${await handle(target)}`,

			[Event.HeroPower]: async (plr, value, handle) =>
				`${await handle(plr)} used their hero power`,

			[Event.TargetSelectionStarts]: async (
				plr,
				[prompt, host, flags],
				handle,
			) =>
				`${await handle(plr)} started selecting a target. [Prompt: "${prompt}", Host: ${host ? await handle(host) : "Game"}, Type: ${flags.targetType !== undefined ? TargetType[flags.targetType] : "All"}, Alignment: ${flags.alignment !== undefined ? Alignment[flags.alignment] : "All"}]`,

			[Event.TargetSelected]: async (plr, [host, target], handle) =>
				`${await handle(plr)} selected ${await handle(target)} [Host: ${host ? await handle(host) : "Game"}]`,

			[Event.CardEvent]: async (plr, [card, event], handle) =>
				`${await handle(card)} said: ${event}`,

			[Event.Dummy]: async (plr, value, handle) => `Dummy Event (Test)`,

			[Event.Eval]: async (plr, value, handle) =>
				`${await handle(plr)} eval'd: ${await handle(value)}`,

			[Event.Input]: async (plr, value, handle) =>
				`${await handle(plr)} typed: ${await handle(value)}`,
		},
🤖 Prompt for AI Agents
In `@config.ts` around lines 375 - 494, The TargetSelectionStarts readableHistory
entry uses truthy checks for flags.targetType and flags.alignment which will
treat valid enum values 0 as missing; update the conditional expressions in the
TargetSelectionStarts handler to use explicit !== undefined checks (e.g.,
flags.targetType !== undefined and flags.alignment !== undefined) when deciding
whether to render TargetType[flags.targetType] or Alignment[flags.alignment] so
the fallback "All" is only used for undefined values; keep the rest of the
handler (plr, prompt, host, handle usage) unchanged.

Addressed in commit 2fca7ec

_⚠️ Potential issue_ | _🔴 Critical_ <details> <summary>🧩 Analysis chain</summary> 🏁 Script executed: ```shell rg -n "enum Alignment|enum TargetType" --type=ts ``` Repository: LunarTides/Hearthstone.js Length of output: 161 --- 🏁 Script executed: ```shell sed -n '75,95p' src/types/other.ts ``` Repository: LunarTides/Hearthstone.js Length of output: 371 --- 🏁 Script executed: ```shell sed -n '437,447p' config.ts ``` Repository: LunarTides/Hearthstone.js Length of output: 467 --- 🏁 Script executed: ```shell rg -n "Event.TargetSelectionStarts" config.ts -A 5 ``` Repository: LunarTides/Hearthstone.js Length of output: 562 --- </details> **Replace enum lookups with explicit `!== undefined` checks in `TargetSelectionStarts`.** The current truthiness checks will fail when `flags.targetType` or `flags.alignment` is `0`, which are valid enum values (`TargetType.Player` and `Alignment.Friendly` respectively). This causes the fallback `"All"` to display incorrectly. <details> <summary>Fix: Use explicit undefined checks</summary> ```diff [Event.TargetSelectionStarts]: async ( plr, [prompt, host, flags], handle, ) => `${await handle(plr)} started selecting a target. [Prompt: "${prompt}", Host: ${host ? await handle(host) : "Game"}, Type: ${flags.targetType !== undefined ? TargetType[flags.targetType] : "All"}, Alignment: ${flags.alignment !== undefined ? Alignment[flags.alignment] : "All"}]`, ``` </details> <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. ```suggestion /** * For each event here, it will show the return value of the function in the history command. * * The `handle` function should be called when dealing with the value. Don't change the `hide` parameter unless you have a good reason to. */ readableHistory: { [Event.FatalDamage]: async (plr, value, handle) => `${await handle(plr)} was dealt fatal damage`, [Event.EndTurn]: async (plr, value, handle) => `${await handle(plr)} ended their turn`, [Event.StartTurn]: async (plr, value, handle) => `${await handle(plr)} started their turn`, [Event.HealthRestored]: async (plr, value, handle) => `${await handle(plr)} restored to <b>${await handle(value)}</b> health`, [Event.UnspentMana]: async (plr, value, handle) => `${await handle(plr)} ended their turn with <b>${await handle(value)}</b> unspent mana`, [Event.GainOverload]: async (plr, value, handle) => `${await handle(plr)} gained <b>${await handle(value)}</b> overload`, [Event.GainHeroAttack]: async (plr, value, handle) => `${await handle(plr)} gained <b>${await handle(value)}</b> attack`, [Event.TakeDamage]: async (plr, value, handle) => `${await handle(plr)} took <b>${await handle(value)}</b> damage`, [Event.PlayCard]: async (plr, value, handle) => `${await handle(plr)} played ${await handle(value)}`, [Event.PlayCardUnsafe]: async (plr, value, handle) => `${await handle(plr)} is trying to play a ${await handle(value)}`, [Event.SummonCard]: async (plr, value, handle) => `${await handle(plr)} summoned a ${await handle(value)}`, [Event.DestroyCard]: async (plr, value, handle) => `${await handle(value)} was destroyed`, [Event.DamageCard]: async (plr, [card, amount], handle) => `${await handle(card)} was dealt <b>${await handle(amount)}</b> damage`, [Event.SilenceCard]: async (plr, value, handle) => `${await handle(value)} was silenced`, [Event.DiscardCard]: async (plr, value, handle) => `${await handle(value)} was discarded`, [Event.CancelCard]: async (plr, [card, ability], handle) => `${await handle(card)}'s <b>${await handle(ability)}</b> was cancelled`, [Event.TradeCard]: async (plr, value, handle) => `${await handle(value)} was traded`, [Event.ForgeCard]: async (plr, value, handle) => `${await handle(value)} was forged`, [Event.FreezeCard]: async (plr, value, handle) => `${await handle(value)} was frozen`, [Event.CreateCard]: async (plr, value, handle) => `${await handle(value)} was created`, [Event.RevealCard]: async (plr, [card, reason], handle) => `${await handle(card)} was revealed due to ${reason}`, [Event.BurnCard]: async (plr, value, handle) => `${await handle(value)} was burned due to a lack of hand space`, [Event.Titan]: async (plr, [titan, ability], handle) => `${await handle(titan)}'s titan ability (${await handle(ability)}) was triggered`, [Event.AddCardToDeck]: async (plr, value, handle) => `${await handle(value)} was added to ${await handle(plr)}'s deck`, [Event.AddCardToHand]: async (plr, value, handle) => `${await handle(value)} was added to ${await handle(plr)}'s hand`, [Event.DrawCard]: async (plr, value, handle) => `${await handle(plr)} drew ${await handle(value)}`, [Event.ChangeLocation]: async (plr, [card, location], handle) => `${await handle(card)}'s location was changed to <b>${await handle(location)}</b>`, [Event.ChangeHero]: async (plr, [oldHero, newHero], handle) => `${await handle(plr)}'s hero has become ${await handle(newHero)}`, [Event.SpellDealsDamage]: async (plr, [target, amount], handle) => `${await handle(target)} dealt <b>${amount}</b> damage`, [Event.Attack]: async (plr, [attacker, target, flags], handle) => `${await handle(attacker)} attacked ${await handle(target)}`, [Event.HeroPower]: async (plr, value, handle) => `${await handle(plr)} used their hero power`, [Event.TargetSelectionStarts]: async ( plr, [prompt, host, flags], handle, ) => `${await handle(plr)} started selecting a target. [Prompt: "${prompt}", Host: ${host ? await handle(host) : "Game"}, Type: ${flags.targetType !== undefined ? TargetType[flags.targetType] : "All"}, Alignment: ${flags.alignment !== undefined ? Alignment[flags.alignment] : "All"}]`, [Event.TargetSelected]: async (plr, [host, target], handle) => `${await handle(plr)} selected ${await handle(target)} [Host: ${host ? await handle(host) : "Game"}]`, [Event.CardEvent]: async (plr, [card, event], handle) => `${await handle(card)} said: ${event}`, [Event.Dummy]: async (plr, value, handle) => `Dummy Event (Test)`, [Event.Eval]: async (plr, value, handle) => `${await handle(plr)} eval'd: ${await handle(value)}`, [Event.Input]: async (plr, value, handle) => `${await handle(plr)} typed: ${await handle(value)}`, }, ``` </details> <!-- suggestion_end --> <details> <summary>🤖 Prompt for AI Agents</summary> ``` In `@config.ts` around lines 375 - 494, The TargetSelectionStarts readableHistory entry uses truthy checks for flags.targetType and flags.alignment which will treat valid enum values 0 as missing; update the conditional expressions in the TargetSelectionStarts handler to use explicit !== undefined checks (e.g., flags.targetType !== undefined and flags.alignment !== undefined) when deciding whether to render TargetType[flags.targetType] or Alignment[flags.alignment] so the fallback "All" is only used for undefined values; keep the rest of the handler (plr, prompt, host, handle usage) unchanged. ``` </details> <!-- fingerprinting:phantom:poseidon:eagle --> <!-- This is an auto-generated comment by CodeRabbit --> ✅ Addressed in commit 2fca7ec
coderabbitai[bot] (Migrated from github.com) reviewed 2026-01-20 14:18:05 +00:00
coderabbitai[bot] (Migrated from github.com) left a comment

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/commands.ts`:
- Around line 377-384: In the handle function, the branch handling Player
instances calls the async method Player.readable() but doesn't await it, causing
a Promise to be returned instead of the resolved string; update the Player
branch in handle (the async function handle(value: unknown, hide: boolean):
Promise<string>) to await value.readable() (i.e., return await value.readable())
so the function returns the resolved string as declared.
🧹 Nitpick comments (1)
src/commands.ts (1)

510-520: Consider typing newValue as string[] for clarity.

The newValue array is populated exclusively with strings returned from handle(), but it's typed as unknown[]. A more precise type would improve readability and type safety.

♻️ Suggested improvement
-				const newValue: unknown[] = [];
+				const newValue: string[] = [];
**Actionable comments posted: 1** <details> <summary>🤖 Fix all issues with AI agents</summary> ``` In `@src/commands.ts`: - Around line 377-384: In the handle function, the branch handling Player instances calls the async method Player.readable() but doesn't await it, causing a Promise to be returned instead of the resolved string; update the Player branch in handle (the async function handle(value: unknown, hide: boolean): Promise<string>) to await value.readable() (i.e., return await value.readable()) so the function returns the resolved string as declared. ``` </details> <details> <summary>🧹 Nitpick comments (1)</summary><blockquote> <details> <summary>src/commands.ts (1)</summary><blockquote> `510-520`: **Consider typing `newValue` as `string[]` for clarity.** The `newValue` array is populated exclusively with strings returned from `handle()`, but it's typed as `unknown[]`. A more precise type would improve readability and type safety. <details> <summary>♻️ Suggested improvement</summary> ```diff - const newValue: unknown[] = []; + const newValue: string[] = []; ``` </details> </blockquote></details> </blockquote></details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
@ -395,2 +392,2 @@
if (!hide || value.owner === player) {
return await showCard(value);
if (!hide || value.owner === game.player) {
return await value.readable();
coderabbitai[bot] (Migrated from github.com) commented 2026-01-20 14:18:04 +00:00

⚠️ Potential issue | 🔴 Critical

Missing await for Player.readable() call.

Player.readable() is an async method (returns Promise<string> per the relevant snippet), but the call on line 379 is not awaited. This will return a Promise object instead of the resolved string.

🐛 Proposed fix
 		const handle = async (value: unknown, hide: boolean): Promise<string> => {
 			if (value instanceof Player) {
-				return value.readable();
+				return await value.readable();
 			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

		const handle = async (value: unknown, hide: boolean): Promise<string> => {
			if (value instanceof Player) {
				return await value.readable();
			}

			if (typeof value === "string") {
				return value;
			}
🤖 Prompt for AI Agents
In `@src/commands.ts` around lines 377 - 384, In the handle function, the branch
handling Player instances calls the async method Player.readable() but doesn't
await it, causing a Promise to be returned instead of the resolved string;
update the Player branch in handle (the async function handle(value: unknown,
hide: boolean): Promise<string>) to await value.readable() (i.e., return await
value.readable()) so the function returns the resolved string as declared.

Addressed in commit bcdcabf

_⚠️ Potential issue_ | _🔴 Critical_ **Missing `await` for `Player.readable()` call.** `Player.readable()` is an async method (returns `Promise<string>` per the relevant snippet), but the call on line 379 is not awaited. This will return a Promise object instead of the resolved string. <details> <summary>🐛 Proposed fix</summary> ```diff const handle = async (value: unknown, hide: boolean): Promise<string> => { if (value instanceof Player) { - return value.readable(); + return await value.readable(); } ``` </details> <!-- suggestion_start --> <details> <summary>📝 Committable suggestion</summary> > ‼️ **IMPORTANT** > Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements. ```suggestion const handle = async (value: unknown, hide: boolean): Promise<string> => { if (value instanceof Player) { return await value.readable(); } if (typeof value === "string") { return value; } ``` </details> <!-- suggestion_end --> <details> <summary>🤖 Prompt for AI Agents</summary> ``` In `@src/commands.ts` around lines 377 - 384, In the handle function, the branch handling Player instances calls the async method Player.readable() but doesn't await it, causing a Promise to be returned instead of the resolved string; update the Player branch in handle (the async function handle(value: unknown, hide: boolean): Promise<string>) to await value.readable() (i.e., return await value.readable()) so the function returns the resolved string as declared. ``` </details> <!-- fingerprinting:phantom:poseidon:ocelot --> <!-- This is an auto-generated comment by CodeRabbit --> ✅ Addressed in commit bcdcabf
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
LunarTides/Hearthstone.js!448
No description provided.