Skip to content

feat: add plan-mode-game hook and supportingFiles installer#481

Open
davila7 wants to merge 1 commit intomainfrom
feat/plan-mode-game-hook
Open

feat: add plan-mode-game hook and supportingFiles installer#481
davila7 wants to merge 1 commit intomainfrom
feat/plan-mode-game-hook

Conversation

@davila7
Copy link
Copy Markdown
Owner

@davila7 davila7 commented Mar 28, 2026

Summary

  • New hook component entertainment/plan-mode-game: launches a browser-based game hub (Dino Runner, Snake, Flappy Bird) while Claude is in Plan Mode. Games react to Claude's tool activity via SSE, pause when Claude needs input, and show a confetti announcement when planning completes.
  • Installer enhancement: implements supportingFiles field processing in installIndividualHook() so hooks with multiple supporting files (scripts, HTML, JS) are downloaded and placed at their declared destinations. Previously only .py and .sh files with matching names were detected.

Test plan

  • Install with npx claude-code-templates@latest --hook entertainment/plan-mode-game
  • Verify all 7 supporting files are downloaded to .claude/hooks/plan-mode-game/
  • Verify hooks are merged into settings (PreToolUse, PostToolUse, Notification)
  • Enter Plan Mode and confirm game launches in browser
  • Confirm activity panel shows tool calls in real-time
  • Confirm game pauses on Notification and resumes on activity
  • Exit Plan Mode and confirm "Claude Finished!" announcement with confetti

🤖 Generated with Claude Code


Summary by cubic

Adds a new entertainment/plan-mode-game hook that launches a small game hub during Plan Mode and reacts to tool activity, plus installer support for multi-file hooks via a new supportingFiles field.

  • New Features
    • Area: components (cli-tool/components/hooks/entertainment/*), CLI (cli-tool/src/index.js).
    • New component: entertainment/plan-mode-game (Dino, Snake, Flappy). Shows live tool activity via SSE, pauses on Notification, and announces completion.
    • Installer: installIndividualHook() now processes supportingFiles, downloads assets to declared destinations, and strips non-settings fields before merge.
    • Runtime: Requires Node.js; starts a local server on port 3456; creates .plan-active, server.pid, and a local .secret HMAC file. No new env vars or external secrets.
    • Catalog: New component added — regenerate docs/components.json.

Written for commit 7835dfb. Summary will update on new commits.

Add entertainment/plan-mode-game hook component that launches a browser-based
game hub (Dino Runner, Snake, Flappy Bird) while Claude plans. Games react to
Claude's activity via SSE, pause on Notification, and show confetti on exit.

Also implement supportingFiles field processing in the hook installer so hooks
with multiple supporting files are properly downloaded and placed at their
declared destinations.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
claude-code-templates Ready Ready Preview Mar 28, 2026 7:42pm

Request Review

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 issues found across 9 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cli-tool/components/hooks/entertainment/plan-mode-game.html">

<violation number="1" location="cli-tool/components/hooks/entertainment/plan-mode-game.html:934">
P1: Avoid rendering SSE-provided activity values via `innerHTML`; this allows HTML/script injection from `tool_name`/`tool_input`.</violation>
</file>

<file name="cli-tool/src/index.js">

<violation number="1" location="cli-tool/src/index.js:1163">
P1: Validate `supportingFiles[].destination` before writing files; current code allows path traversal/absolute-path writes outside the Claude directory.</violation>
</file>

<file name="cli-tool/components/hooks/entertainment/plan-mode-game-start.sh">

<violation number="1" location="cli-tool/components/hooks/entertainment/plan-mode-game-start.sh:24">
P2: Persist the background server PID after startup; otherwise the existing PID-file guard never works and duplicate servers can be launched.</violation>
</file>

<file name="cli-tool/components/hooks/entertainment/plan-mode-game-server.js">

<violation number="1" location="cli-tool/components/hooks/entertainment/plan-mode-game-server.js:25">
P2: Do not allow wildcard CORS on this local control server; it lets arbitrary sites call local state-changing endpoints.</violation>

<violation number="2" location="cli-tool/components/hooks/entertainment/plan-mode-game-server.js:116">
P1: Validate/coerce shared scores to numbers before signing; current direct HTML interpolation allows XSS in the verification page.</violation>
</file>

<file name="cli-tool/components/hooks/entertainment/plan-mode-game-announce.sh">

<violation number="1" location="cli-tool/components/hooks/entertainment/plan-mode-game-announce.sh:9">
P2: Add a short timeout to the `/done` POST so this hook cannot block when the local game server is unresponsive.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

item.className='activity-item new';
const icon=TOOL_ICONS[toolName]||TOOL_ICONS.default;
const time=new Date().toLocaleTimeString('en',{hour12:false,hour:'2-digit',minute:'2-digit',second:'2-digit'});
item.innerHTML=`<div class="activity-icon">${icon}</div><div class="activity-content"><div class="activity-tool">${toolName}</div>${detail?`<div class="activity-detail">${detail}</div>`:''}</div><div class="activity-time">${time}</div>`;
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Avoid rendering SSE-provided activity values via innerHTML; this allows HTML/script injection from tool_name/tool_input.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cli-tool/components/hooks/entertainment/plan-mode-game.html, line 934:

<comment>Avoid rendering SSE-provided activity values via `innerHTML`; this allows HTML/script injection from `tool_name`/`tool_input`.</comment>

<file context>
@@ -0,0 +1,1076 @@
+  item.className='activity-item new';
+  const icon=TOOL_ICONS[toolName]||TOOL_ICONS.default;
+  const time=new Date().toLocaleTimeString('en',{hour12:false,hour:'2-digit',minute:'2-digit',second:'2-digit'});
+  item.innerHTML=`<div class="activity-icon">${icon}</div><div class="activity-content"><div class="activity-tool">${toolName}</div>${detail?`<div class="activity-detail">${detail}</div>`:''}</div><div class="activity-time">${time}</div>`;
+  list.appendChild(item);
+  list.scrollTop=list.scrollHeight;
</file context>
Fix with Cubic

const sfResponse = await fetch(fileUrl);
if (sfResponse.ok) {
const sfContent = await sfResponse.text();
additionalFiles[sf.destination] = {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Validate supportingFiles[].destination before writing files; current code allows path traversal/absolute-path writes outside the Claude directory.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cli-tool/src/index.js, line 1163:

<comment>Validate `supportingFiles[].destination` before writing files; current code allows path traversal/absolute-path writes outside the Claude directory.</comment>

<file context>
@@ -1150,9 +1150,34 @@ async function installIndividualHook(hookName, targetDir, options) {
+          const sfResponse = await fetch(fileUrl);
+          if (sfResponse.ok) {
+            const sfContent = await sfResponse.text();
+            additionalFiles[sf.destination] = {
+              content: sfContent,
+              executable: sf.executable === true
</file context>
Fix with Cubic

req.on('end', () => {
try {
const { dino, snake, flappy } = JSON.parse(body);
const payload = { d: dino || 0, s: snake || 0, f: flappy || 0, t: Date.now() };
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Validate/coerce shared scores to numbers before signing; current direct HTML interpolation allows XSS in the verification page.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cli-tool/components/hooks/entertainment/plan-mode-game-server.js, line 116:

<comment>Validate/coerce shared scores to numbers before signing; current direct HTML interpolation allows XSS in the verification page.</comment>

<file context>
@@ -0,0 +1,225 @@
+    req.on('end', () => {
+      try {
+        const { dino, snake, flappy } = JSON.parse(body);
+        const payload = { d: dino || 0, s: snake || 0, f: flappy || 0, t: Date.now() };
+        const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');
+        const sig = crypto.createHmac('sha256', hmacSecret).update(payloadB64).digest('hex').slice(0, 16);
</file context>
Fix with Cubic

fi

# Start server in background
nohup node "$SCRIPT_DIR/server.js" > /dev/null 2>&1 &
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Persist the background server PID after startup; otherwise the existing PID-file guard never works and duplicate servers can be launched.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cli-tool/components/hooks/entertainment/plan-mode-game-start.sh, line 24:

<comment>Persist the background server PID after startup; otherwise the existing PID-file guard never works and duplicate servers can be launched.</comment>

<file context>
@@ -0,0 +1,35 @@
+fi
+
+# Start server in background
+nohup node "$SCRIPT_DIR/server.js" > /dev/null 2>&1 &
+
+# Wait for server to be ready
</file context>
Fix with Cubic

const hmacSecret = getSecret();

const server = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Do not allow wildcard CORS on this local control server; it lets arbitrary sites call local state-changing endpoints.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cli-tool/components/hooks/entertainment/plan-mode-game-server.js, line 25:

<comment>Do not allow wildcard CORS on this local control server; it lets arbitrary sites call local state-changing endpoints.</comment>

<file context>
@@ -0,0 +1,225 @@
+const hmacSecret = getSecret();
+
+const server = http.createServer((req, res) => {
+  res.setHeader('Access-Control-Allow-Origin', '*');
+  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
+  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
</file context>
Fix with Cubic

PORT=3456

# Send announcement
curl -s -X POST "http://localhost:$PORT/done" -d "Claude Finished!" > /dev/null 2>&1
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Add a short timeout to the /done POST so this hook cannot block when the local game server is unresponsive.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cli-tool/components/hooks/entertainment/plan-mode-game-announce.sh, line 9:

<comment>Add a short timeout to the `/done` POST so this hook cannot block when the local game server is unresponsive.</comment>

<file context>
@@ -0,0 +1,18 @@
+PORT=3456
+
+# Send announcement
+curl -s -X POST "http://localhost:$PORT/done" -d "Claude Finished!" > /dev/null 2>&1
+
+# Remove plan-active flag
</file context>
Suggested change
curl -s -X POST "http://localhost:$PORT/done" -d "Claude Finished!" > /dev/null 2>&1
curl -s --max-time 1 -X POST "http://localhost:$PORT/done" -d "Claude Finished!" > /dev/null 2>&1 || true
Fix with Cubic

@bitreonx
Copy link
Copy Markdown
Contributor

PR #481 Review: Plan Mode Game Hook

Overview

This PR introduces an entertaining and innovative feature that launches browser-based mini-games (Dino Runner, Snake, Flappy Bird) while Claude is in Plan Mode, with real-time activity tracking via SSE.

Usefulness Assessment: ⭐⭐⭐⭐ (4/5)

Strengths

1. Creative Developer Experience Enhancement

  • Transforms waiting time during Claude's planning into an engaging experience
  • Real-time activity feed provides transparency into Claude's operations
  • Gamification makes the planning process more enjoyable

2. Well-Architected Implementation

  • Clean separation of concerns (server, hooks, UI)
  • SSE for real-time communication is appropriate
  • Proper state management with flag files and PID tracking
  • Score persistence with localStorage
  • HMAC-signed URLs for verified score sharing

3. Non-Intrusive Design

  • Fast-path exits when not in plan mode (minimal overhead)
  • Automatic pause/resume on user input needs
  • Locked state when not in plan mode prevents confusion
  • Games only activate during actual planning

4. Supporting Files Feature

  • Introduces supportingFiles installer mechanism (valuable for complex hooks)
  • Enables multi-file hook installations
  • Sets precedent for future complex hook components

Weaknesses & Security Concerns

Critical Issues (P1):

  1. XSS Vulnerability in Activity Feed (Line 934)

    • innerHTML renders SSE-provided tool_name/tool_input without sanitization
    • Malicious tool names could inject scripts
    • Impact: High - could compromise user's browser session
  2. Path Traversal in supportingFiles (index.js:1163)

    • No validation of destination paths before writing
    • Could write files outside .claude directory
    • Impact: High - arbitrary file write vulnerability
  3. XSS in Score Verification (server.js:116)

    • Scores not validated/coerced to numbers before HTML interpolation
    • Impact: Medium-High - XSS in verification page

Important Issues (P2):

  1. Missing PID Persistence (start.sh:24)

    • Server starts but PID never written to file
    • Duplicate server prevention doesn't work
    • Impact: Medium - resource waste, port conflicts
  2. Wildcard CORS (server.js:25)

    • Access-Control-Allow-Origin: * on local control server
    • Arbitrary sites can call state-changing endpoints
    • Impact: Medium - CSRF vulnerability
  3. No Timeout on Cleanup (announce.sh:9)

    • /done POST can block indefinitely if server unresponsive
    • Impact: Low-Medium - hook can hang

Recommendations

Immediate Fixes Required

// Fix 1: Sanitize activity rendering
function escapeHtml(str) {
  const div = document.createElement('div');
  div.textContent = str;
  return div.innerHTML;
}
// Use: item.textContent or escapeHtml() instead of innerHTML
// Fix 2: Validate destination paths
const path = require('path');
const claudeDir = path.resolve(targetDir, '.claude');
const destPath = path.resolve(targetDir, sf.destination);
if (!destPath.startsWith(claudeDir)) {
  throw new Error(`Invalid destination path: ${sf.destination}`);
}
// Fix 3: Validate scores
const payload = { 
  d: Math.max(0, parseInt(dino) || 0),
  s: Math.max(0, parseInt(snake) || 0),
  f: Math.max(0, parseInt(flappy) || 0),
  t: Date.now()
};
# Fix 4: Persist PID
nohup node "$SCRIPT_DIR/server.js" > /dev/null 2>&1 &
echo $! > "$PID_FILE"
// Fix 5: Restrict CORS
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3456');
# Fix 6: Add timeout
curl -s --max-time 1 -X POST "http://localhost:$PORT/done" -d "Claude Finished!" > /dev/null 2>&1 || true

Future Improvements

  1. Configuration Options

    • Allow users to disable/enable specific games
    • Configurable port number
    • Theme customization
  2. Enhanced Activity Tracking

    • Filter/search activity log
    • Export activity history
    • Performance metrics (tools/sec)
  3. Multiplayer/Social Features

    • Global leaderboards (opt-in)
    • Share game sessions
    • Team challenges
  4. Additional Games

    • Tetris, Pong, or other classics
    • Custom game plugin system
    • Community-contributed games
  5. Accessibility

    • Keyboard navigation improvements
    • Screen reader support
    • High contrast mode
    • Reduced motion option
  6. Performance Optimizations

    • Canvas rendering optimizations
    • Debounce activity updates
    • Lazy load game assets
    • Service worker for offline play
  7. Better Error Handling

    • Graceful degradation if Node.js unavailable
    • Port conflict resolution
    • Connection retry logic
    • User-friendly error messages

Conclusion

This PR is highly creative and adds genuine value to the developer experience. The concept is sound and the implementation demonstrates good architectural thinking. However, security issues must be addressed before merging.

Recommendation: Approve with Required Changes

The security vulnerabilities are straightforward to fix and don't require architectural changes. Once the 6 identified issues are resolved, this feature will be a delightful addition to the project.

Impact on Project

  • Positive: Unique differentiator, improves UX during wait times
  • Risk: Security issues if not fixed
  • Maintenance: Low - self-contained feature
  • Innovation: High - sets precedent for interactive hooks

Overall Assessment: Innovative feature with solid execution that needs security hardening before merge. The supportingFiles mechanism is particularly valuable for future complex hooks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants