Skip to content

Comments

feat: document review system and workflow enforcement#334

Open
obra wants to merge 55 commits intomainfrom
dev
Open

feat: document review system and workflow enforcement#334
obra wants to merge 55 commits intomainfrom
dev

Conversation

@obra
Copy link
Owner

@obra obra commented Jan 23, 2026

Summary

  • Document review system: Adds spec and plan document review loops to catch TODOs, incomplete sections, and deferred content before implementation
  • Workflow enforcement: Brainstorming must transition to writing-plans (not platform planning); subagent-driven-development is required on capable harnesses
  • Directory restructuring: Specs now go to docs/superpowers/specs/, plans to docs/superpowers/plans/

Document Review System

  • Spec document reviewer (skills/brainstorming/spec-document-reviewer-prompt.md) - reviews specs after brainstorming
  • Plan document reviewer (skills/writing-plans/plan-document-reviewer-prompt.md) - reviews plans chunk-by-chunk
  • Iterative loops: fix → re-review → repeat until approved
  • Reviewers are advisory with escalation after 5 iterations

Key Files

  • skills/brainstorming/SKILL.md - added spec review loop
  • skills/writing-plans/SKILL.md - added plan review loop, chunk boundaries, checkbox syntax on steps
  • tests/claude-code/test-document-review-system.sh - integration test that creates spec with errors and verifies reviewer catches them

Test plan

  • Integration test passes - creates spec with intentional errors (TODO, "specified later"), verifies reviewer catches them
  • Manual test: run brainstorming skill, verify spec review loop triggers
  • Manual test: run writing-plans skill, verify chunk-by-chunk plan review triggers

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Visual Brainstorming Companion: browser-based interactive mockup/feedback tool.
    • Document Review System: automated spec and plan review loops that iterate until approval.
  • Documentation

    • Clarified Instruction Priority (user → skills → defaults).
    • Moved and reorganized plans/specs into a unified docs location and updated guides for the new workflows.
  • Tests

    • Added integration tests and workflows covering the visual companion and review loops.

@coderabbitai
Copy link

coderabbitai bot commented Jan 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Visual Brainstorming Companion (Node.js server, browser helper, UI frame, shell utilities, tests), introduces Spec and Plan Document Reviewer prompts and review loops, moves plans/specs into docs/superpowers/, updates multiple skill docs to enforce review/review-loop and checkbox task syntax, and updates tests and paths accordingly.

Changes

Cohort / File(s) Summary
Gitignore & Manifests
/.gitignore, lib/brainstorm-server/package.json, tests/brainstorm-server/package.json
Ignore additions; new package manifests for brainstorm-server and its tests with chokidar, express, ws.
Release Notes
RELEASE-NOTES.md
Added Unreleased section documenting restructuring, Visual Brainstorming, review loops, fixes, and improvements.
Brainstorm Server — Core
lib/brainstorm-server/index.js, lib/brainstorm-server/helper.js, lib/brainstorm-server/frame-template.html
New Express + WebSocket server serving newest screen, injecting helper.js; helper captures clicks/forms/inputs, queues events, exposes window.brainstorm; frame template UI added.
Brainstorm Server — Utilities
lib/brainstorm-server/start-server.sh, lib/brainstorm-server/stop-server.sh, lib/brainstorm-server/wait-for-feedback.sh
Session-isolated start/stop scripts, log-waiting utility that polls for send-to-claude events and manages per-session dirs/PIDs.
Brainstorming Docs & Plan
docs/plans/2026-01-17-visual-brainstorming.md, skills/brainstorming/visual-companion.md, skills/brainstorming/SKILL.md
Added visual-brainstorming plan and guide; updated brainstorming skill (spec destination change, Spec Review Loop, Visual Companion section, scope/decomposition guidance).
Review System Specs & Prompts
docs/superpowers/specs/2026-01-22-document-review-system-design.md, docs/superpowers/plans/2026-01-22-document-review-system.md, skills/brainstorming/spec-document-reviewer-prompt.md, skills/writing-plans/plan-document-reviewer-prompt.md
New design doc, plan, and reviewer prompt templates; defines SPEC REVIEW LOOP and PLAN REVIEW LOOP workflows and reviewer output formats.
Writing Plans & Skills
skills/writing-plans/SKILL.md, skills/requesting-code-review/SKILL.md, skills/subagent-driven-development/SKILL.md, skills/using-superpowers/SKILL.md
Redirected default save paths to docs/superpowers/plans/; introduced checkbox task syntax, Plan Review Loop, subagent-driven execution guidance, model-selection and instruction-priority documentation.
Tests — Brainstorm Server
tests/brainstorm-server/server.test.js, tests/brainstorm-server/package.json
New integration tests for server startup, HTTP/WebSocket behavior, helper injection, reload signaling, fragment/full-document handling; test manifest added.
Tests & Prompts — Path Updates
tests/claude-code/*, tests/explicit-skill-requests/*, tests/skill-triggering/*, tests/claude-code/test-helpers.sh
Updated many tests and prompt files to reference docs/superpowers/plans/ instead of docs/plans/; added document-review integration test.
Skill Prompts & Guidance
skills/brainstorming/spec-document-reviewer-prompt.md, skills/writing-plans/plan-document-reviewer-prompt.md, skills/subagent-driven-development/*
Added/updated reviewer and implementer prompts with new review/reporting status conventions and code-quality guidance.
Misc
docs/plans/*, lib/brainstorm-server/* (new files), tests/*
Large set of new docs/plans, server files, shell utilities, and tests; numerous path updates across tests and prompts.

Sequence Diagram(s)

sequenceDiagram
    participant Claude as Claude Instance
    participant Server as Brainstorm Server
    participant FileSystem as File System
    participant Browser as Browser Helper
    participant User as End User

    Claude->>FileSystem: write screen.html to /tmp/brainstorm (or project dir)
    FileSystem-->>Server: chokidar add/change event
    Server->>FileSystem: read newest .html
    Server->>Server: inject helper.js (wrap fragment in frame if needed)
    Browser->>Server: WebSocket connect
    User->>Browser: interact (click / form / input)
    Browser->>Browser: capture, queue, debounce events
    Browser->>Server: send event via WebSocket
    Server->>Server: log event to stdout (JSON)
    Claude->>Server: wait-for-feedback / poll .server.log
    loop edits
        Claude->>FileSystem: update .html
        FileSystem-->>Server: change event
        Server->>Browser: broadcast reload
        Browser->>Browser: reload page
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped into a helper.js night,

Poked sockets till the screens shone bright.
Specs inspected, each plan in rows,
I nibbled bugs and watched reloads.
Carrots, code, and brainstorm light — delight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: document review system and workflow enforcement' directly and clearly summarizes the main changes: introducing document review systems and enforcing workflow transitions.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🤖 Fix all issues with AI agents
In `@docs/README.opencode.md`:
- Around line 171-173: The fenced code blocks containing the examples "use skill
tool to list skills" and "use skill tool to load superpowers/brainstorming" are
missing language identifiers; update those backtick blocks to include a language
such as text (e.g., change ``` to ```text) for each occurrence (including the
second example referenced at 179-181) so markdownlint MD040 no longer flags
them. Ensure every fenced block that shows those exact example lines is updated
consistently to include the language identifier.

In `@docs/superpowers/specs/2026-01-22-document-review-system-design.md`:
- Around line 28-39: The fenced code block that starts with "## Spec Review" is
missing a language identifier (MD040); update both opening triple-backtick
fences to include a language (e.g., change ``` to ```text) for the block
containing "## Spec Review" and the later fenced block containing the workflow
line "brainstorming -> spec -> SPEC REVIEW LOOP -> writing-plans -> plan -> PLAN
REVIEW LOOP -> implementation" so markdownlint stops flagging them; ensure you
update the opening fences for the blocks referenced in the diff and the other
occurrence at the section noted as "Also applies to: 83-85".

In `@hooks/hooks.json`:
- Line 9: The command in hooks.json uses an unquoted expansion of
CLAUDE_PLUGIN_ROOT which can break when the path contains spaces; update the
"command" value from "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh" to use
single quotes around the expansion (i.e.,
'${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh') so the shell receives the path
as a single argument and the session-start.sh hook runs reliably.

In `@lib/brainstorm-server/helper.js`:
- Around line 15-20: The ws.onmessage handler currently calls
JSON.parse(msg.data) without protection; wrap the parse in a try/catch inside
the ws.onmessage function to prevent malformed JSON from throwing and breaking
the handler, e.g., try to parse msg.data, verify the resulting object exists and
has data.type === 'reload' before calling window.location.reload(), and in the
catch block log the parsing error (using console.error or a logger) and return
silently so the handler continues to run safely.

In `@lib/brainstorm-server/index.js`:
- Around line 55-63: The ws.on('message') handler currently calls JSON.parse
directly which can throw on malformed payloads and crash the server; update the
message handler inside the wss.on('connection' /* connection callback */) to
wrap the JSON.parse(data.toString()) call in a try/catch, handle parse errors by
logging the parse failure (include the raw data and error) via console.error or
process logger, and skip processing that message (optionally send an error
response over ws) instead of letting the exception bubble up and crash the
process; keep the rest of the successful-path behavior (building event and
console.log) unchanged.

In `@lib/brainstorm-server/package.json`:
- Around line 1-11: Update package.json to set "private": true at the top level
and bump the dependency versions: change "express" to a version >= 4.20.0 (or
latest 4.x/5.x) and "ws" to >= 8.17.1 in the "dependencies" object (leave
"chokidar" unchanged); after editing package.json run npm install (or yarn
install) to update package-lock.json/yarn.lock and verify no breaking changes.

In `@lib/brainstorm-server/start-server.sh`:
- Around line 33-45: The timeout branch currently exits without cleaning up the
background server and temporary directory; capture the background server PID
when launching it (e.g., assign $! to SERVER_PID where the server is started)
and on timeout kill that PID (first try kill "$SERVER_PID" then fallback to kill
-9 if needed), wait for it to exit, and remove the temp directory (e.g., rm -rf
"$TEMP_DIR" or "$TMP_DIR") before printing the JSON error and exiting with
status 1; also handle cases where SERVER_PID or the temp-dir variable may be
empty to avoid errors.

In `@tests/brainstorm-server/server.test.js`:
- Around line 8-10: The test is setting BRAINSTORM_SCREEN and a TEST_SCREEN path
but the server expects BRAINSTORM_DIR and the test does not create the screen
directory, causing ENOENT; update the test to export the correct env var (set
process.env.BRAINSTORM_DIR to the directory portion of TEST_SCREEN or adjust
SERVER_PATH usage) and ensure the test creates the directory for TEST_SCREEN
before any file writes (mkdir -p semantics) and clean it up after; apply the
same changes where TEST_SCREEN/port/env are used (the other occurrences around
the blocks referenced) so the server reads the right env and the directory
exists during reload tests.

In `@tests/claude-code/test-document-review-system.sh`:
- Around line 92-98: The failing exit code of the pipeline is overwritten by the
subsequent echo, so capture the exit status immediately after running the
command and before any other commands; assign the exit status of the `timeout
120 claude -p "$PROMPT" --permission-mode bypassPermissions 2>&1 | tee
"$OUTPUT_FILE"` pipeline (i.e., `$?`) to a local variable (e.g., EXIT_CODE)
right after it fails, then use that variable in the `echo "EXECUTION FAILED
(exit code: $EXIT_CODE)"` and the `exit $EXIT_CODE` calls; reference the
pipeline invocation and the `$?` usage in your fix.
🧹 Nitpick comments (9)
.gitattributes (1)

1-17: LGTM! Consider adding a default text normalization rule.

The line-ending configuration is correct:

  • LF for shell scripts ensures cross-platform compatibility
  • LF for .cmd files is appropriate given their polyglot nature (parsed by both cmd and bash)
  • Text and binary file coverage is comprehensive

Optional enhancement: Consider adding a default text normalization rule at the beginning of the file:

+# Auto-detect text files and normalize line endings
+* text=auto
+
 # Ensure shell scripts always have LF line endings
 *.sh text eol=lf

This provides defense-in-depth for file types not explicitly listed.

lib/brainstorm-server/stop-server.sh (1)

14-19: Consider validating SCREEN_DIR before rm -rf.

The script deletes $SCREEN_DIR unconditionally when the PID file exists. If this script is ever invoked with an incorrect or malicious path argument, rm -rf could cause unintended data loss.

Consider adding a sanity check to ensure SCREEN_DIR is within an expected parent directory (e.g., a temp directory or a known session root):

Suggested safety check
 if [[ -f "$PID_FILE" ]]; then
   pid=$(cat "$PID_FILE")
   kill "$pid" 2>/dev/null
+  # Safety: only remove if SCREEN_DIR looks like a session directory
+  if [[ "$SCREEN_DIR" == /tmp/* || "$SCREEN_DIR" == */brainstorm-session-* ]]; then
     # Clean up session directory
     rm -rf "$SCREEN_DIR"
+  else
+    echo '{"warning": "skipped cleanup - unexpected directory path"}'
+  fi
   echo '{"status": "stopped"}'
skills/writing-plans/plan-document-reviewer-prompt.md (1)

9-9: Add language specifier to fenced code block.

The code block lacks a language specifier. Since this contains a YAML-like task tool definition, consider adding yaml for syntax highlighting.

Suggested fix
-```
+```yaml
 Task tool (general-purpose):
lib/brainstorm-server/frame-template.html (2)

1-2: Add lang attribute to <html> element for accessibility.

The <html> element is missing the lang attribute, which is important for screen readers and accessibility compliance (WCAG 2.1 Success Criterion 3.1.1).

Suggested fix
 <!DOCTYPE html>
-<html>
+<html lang="en">

247-256: Add defensive check for brainstorm object.

The send() function calls brainstorm.sendToClaude(payload) without checking if the brainstorm object exists. If helper.js fails to load or inject the global, this will throw a runtime error.

Suggested fix
     function send() {
       const feedbackEl = document.getElementById('feedback');
       const feedback = feedbackEl.value.trim();
       const payload = {};
       if (selectedChoice) payload.choice = selectedChoice;
       if (feedback) payload.feedback = feedback;
       if (Object.keys(payload).length === 0) return;
+      if (typeof brainstorm === 'undefined' || !brainstorm.sendToClaude) {
+        console.error('Brainstorm helper not loaded');
+        return;
+      }
       brainstorm.sendToClaude(payload);
       feedbackEl.value = '';
     }
lib/brainstorm-server/wait-for-feedback.sh (1)

16-27: Consider handling missing log file and adding a timeout.

Two potential reliability concerns:

  1. Missing log file: If .server.log doesn't exist when the script starts, wc -l returns 0 (good), but tail will silently fail on each poll iteration until the file is created. Consider adding an initial wait for the file.

  2. No timeout: The script polls indefinitely. If the server crashes or the user abandons the session, this script hangs forever. Consider adding an optional timeout parameter.

♻️ Suggested improvements
 # Record current position in log file
+# Wait for log file to exist (server may still be starting)
+TIMEOUT=10
+while [[ ! -f "$LOG_FILE" ]] && (( TIMEOUT-- > 0 )); do
+  sleep 0.5
+done
+
 LOG_POS=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)
 
+# Optional: Add max iterations for timeout (e.g., 1800 iterations = 6 minutes)
+MAX_ITERATIONS=${2:-0}  # 0 = no limit
+ITERATION=0
+
 # Poll for new lines containing the event
 while true; do
   RESULT=$(tail -n +$((LOG_POS + 1)) "$LOG_FILE" 2>/dev/null | grep -m 1 "send-to-claude")
   if [[ -n "$RESULT" ]]; then
     echo "$RESULT"
     exit 0
   fi
+  if [[ $MAX_ITERATIONS -gt 0 ]] && (( ++ITERATION >= MAX_ITERATIONS )); then
+    echo '{"error": "Timeout waiting for feedback"}' >&2
+    exit 2
+  fi
   sleep 0.2
 done
skills/brainstorming/visual-companion.md (1)

22-30: Clarify the relationship between wait-for-feedback.sh and TaskOutput.

The quick start shows two waiting mechanisms:

  • Line 23: wait-for-feedback.sh (shell-based polling)
  • Line 28: TaskOutput(task_id, block=true, timeout=600000) (tool-based blocking)

It's unclear how these interact. Does TaskOutput read from the watcher's output, or are these alternative approaches? A brief note explaining the connection would help Claude (and developers) understand the workflow.

skills/brainstorming/spec-document-reviewer-prompt.md (1)

9-9: Add a language identifier to the fenced code block.

The static analysis tool flagged MD040 for missing language specification. Since this is a prompt template, consider using text or markdown as the language identifier.

♻️ Suggested fix
-```
+```text
 Task tool (general-purpose):
lib/brainstorm-server/helper.js (1)

72-88: Single debounce timer loses rapid cross-field input events.

The shared inputTimeout variable means typing in field A, then quickly switching to field B (within 500ms), cancels field A's event. If multiple form fields are expected, consider per-element debouncing.

Per-element debounce alternative
   // Auto-capture input changes (debounced)
-  let inputTimeout = null;
+  const inputTimeouts = new WeakMap();
   document.addEventListener('input', (e) => {
     const target = e.target;
     if (!target.matches('input, textarea, select')) return;

-    clearTimeout(inputTimeout);
-    inputTimeout = setTimeout(() => {
+    clearTimeout(inputTimeouts.get(target));
+    inputTimeouts.set(target, setTimeout(() => {
       send({
         type: 'input',
         name: target.name || null,
         id: target.id || null,
         value: target.value,
         inputType: target.type || target.tagName.toLowerCase()
       });
-    }, 500); // 500ms debounce
+    }, 500));
   });

Comment on lines 171 to 173
```
use find_skills tool
use skill tool to list skills
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add fenced code block language identifiers (MD040).
Markdownlint will continue to flag these examples without a language.

🧹 Suggested fix
-```
+```text
 use skill tool to list skills

```diff
-```
+```text
 use skill tool to load superpowers/brainstorming
</details>


Also applies to: 179-181

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

171-171: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In @docs/README.opencode.md around lines 171 - 173, The fenced code blocks
containing the examples "use skill tool to list skills" and "use skill tool to
load superpowers/brainstorming" are missing language identifiers; update those
backtick blocks to include a language such as text (e.g., change totext)
for each occurrence (including the second example referenced at 179-181) so
markdownlint MD040 no longer flags them. Ensure every fenced block that shows
those exact example lines is updated consistently to include the language
identifier.


</details>

<!-- fingerprinting:phantom:poseidon:eagle -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment on lines +28 to +39
**Output format:**
```
## Spec Review

**Status:** Approved | Issues Found

**Issues (if any):**
- [Section X]: [issue] - [why it matters]

**Recommendations (advisory):**
- [suggestions that don't block approval]
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add language identifiers to fenced code blocks (MD040).
markdownlint flags code fences without a language spec.

🔧 Proposed fix
-```
+```text
 ## Spec Review

 **Status:** Approved | Issues Found
@@
-```
+```text
 brainstorming -> spec -> SPEC REVIEW LOOP -> writing-plans -> plan -> PLAN REVIEW LOOP -> implementation
</details>



Also applies to: 83-85

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

29-29: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

In @docs/superpowers/specs/2026-01-22-document-review-system-design.md around
lines 28 - 39, The fenced code block that starts with "## Spec Review" is
missing a language identifier (MD040); update both opening triple-backtick
fences to include a language (e.g., change totext) for the block
containing "## Spec Review" and the later fenced block containing the workflow
line "brainstorming -> spec -> SPEC REVIEW LOOP -> writing-plans -> plan -> PLAN
REVIEW LOOP -> implementation" so markdownlint stops flagging them; ensure you
update the opening fences for the blocks referenced in the diff and the other
occurrence at the section noted as "Also applies to: 83-85".


</details>

<!-- fingerprinting:phantom:poseidon:eagle -->

<!-- This is an auto-generated comment by CodeRabbit -->

hooks/hooks.json Outdated
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start.sh"
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Quote CLAUDE_PLUGIN_ROOT to avoid path splitting.

Claude Code expands variables before shell execution, so unquoted paths with spaces will break the hook. Please wrap the variable in single quotes. Based on learnings, this is required for reliable hook execution.

✅ Proposed fix
-            "command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"
+            "command": "'${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh'"
📝 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.

Suggested change
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"
"command": "'${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh'"
🤖 Prompt for AI Agents
In `@hooks/hooks.json` at line 9, The command in hooks.json uses an unquoted
expansion of CLAUDE_PLUGIN_ROOT which can break when the path contains spaces;
update the "command" value from "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"
to use single quotes around the expansion (i.e.,
'${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh') so the shell receives the path
as a single argument and the session-start.sh hook runs reliably.

Comment on lines +15 to +20
ws.onmessage = (msg) => {
const data = JSON.parse(msg.data);
if (data.type === 'reload') {
window.location.reload();
}
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add error handling for WebSocket message parsing.

If the server sends malformed JSON, JSON.parse will throw and crash the handler silently. This could break the reload functionality.

Proposed fix
   ws.onmessage = (msg) => {
-    const data = JSON.parse(msg.data);
-    if (data.type === 'reload') {
-      window.location.reload();
+    try {
+      const data = JSON.parse(msg.data);
+      if (data.type === 'reload') {
+        window.location.reload();
+      }
+    } catch (e) {
+      console.warn('Invalid message from server:', e);
     }
   };
📝 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.

Suggested change
ws.onmessage = (msg) => {
const data = JSON.parse(msg.data);
if (data.type === 'reload') {
window.location.reload();
}
};
ws.onmessage = (msg) => {
try {
const data = JSON.parse(msg.data);
if (data.type === 'reload') {
window.location.reload();
}
} catch (e) {
console.warn('Invalid message from server:', e);
}
};
🤖 Prompt for AI Agents
In `@lib/brainstorm-server/helper.js` around lines 15 - 20, The ws.onmessage
handler currently calls JSON.parse(msg.data) without protection; wrap the parse
in a try/catch inside the ws.onmessage function to prevent malformed JSON from
throwing and breaking the handler, e.g., try to parse msg.data, verify the
resulting object exists and has data.type === 'reload' before calling
window.location.reload(), and in the catch block log the parsing error (using
console.error or a logger) and return silently so the handler continues to run
safely.

Comment on lines 55 to 63
wss.on('connection', (ws) => {
clients.add(ws);
ws.on('close', () => clients.delete(ws));

ws.on('message', (data) => {
// User interaction event - write to stdout for Claude
const event = JSON.parse(data.toString());
console.log(JSON.stringify({ source: 'user-event', ...event }));
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check file existence and view the specific lines with context
if [ -f lib/brainstorm-server/index.js ]; then
  echo "=== Lines 50-70 of lib/brainstorm-server/index.js ==="
  sed -n '50,70p' lib/brainstorm-server/index.js | cat -n
  echo ""
  echo "=== Checking for error handlers on ws/wss objects ==="
  rg -n "ws\.on\('error'|wss\.on\('error'|\.on\('error'" lib/brainstorm-server/index.js
else
  echo "File not found"
fi

Repository: obra/superpowers

Length of output: 951


Guard against invalid JSON in WebSocket messages.
A malformed client payload will throw on JSON.parse and crash the server process.

🔧 Proposed fix
   ws.on('message', (data) => {
     // User interaction event - write to stdout for Claude
-    const event = JSON.parse(data.toString());
-    console.log(JSON.stringify({ source: 'user-event', ...event }));
+    let event;
+    try {
+      event = JSON.parse(data.toString());
+    } catch (err) {
+      console.warn(JSON.stringify({ type: 'invalid-user-event', error: err.message }));
+      return;
+    }
+    console.log(JSON.stringify({ source: 'user-event', ...event }));
   });
🤖 Prompt for AI Agents
In `@lib/brainstorm-server/index.js` around lines 55 - 63, The ws.on('message')
handler currently calls JSON.parse directly which can throw on malformed
payloads and crash the server; update the message handler inside the
wss.on('connection' /* connection callback */) to wrap the
JSON.parse(data.toString()) call in a try/catch, handle parse errors by logging
the parse failure (include the raw data and error) via console.error or process
logger, and skip processing that message (optionally send an error response over
ws) instead of letting the exception bubble up and crash the process; keep the
rest of the successful-path behavior (building event and console.log) unchanged.

Comment on lines +1 to +11
{
"name": "brainstorm-server",
"version": "1.0.0",
"description": "Visual brainstorming companion server for Claude Code",
"main": "index.js",
"dependencies": {
"chokidar": "^3.5.3",
"express": "^4.18.2",
"ws": "^8.14.2"
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

chokidar 3.5.3 security vulnerabilities CVE

💡 Result:

No CVEs are currently mapped as direct vulnerabilities in chokidar@3.5.3 in major vulnerability databases (i.e., no “package chokidar is vulnerable” CVE entries for that version). Snyk reports no direct vulnerabilities for chokidar (including the 3.x line). [1] Meterian likewise lists no known vulnerabilities for chokidar@3.5.3. [2]

Notes that often cause confusion:

  • Older chokidar versions could pull in vulnerable dependencies (example: CVE-2020-28469 via glob-parent), and upgrading away from older chokidar was cited as mitigation. [3]
  • Separate from CVEs, there have been typosquatting/malicious packages impersonating chokidar (e.g., look-alike package names). These are supply-chain threats, not CVEs in the legit chokidar package. [4]

Sources: Snyk chokidar package page [1]; Meterian chokidar@3.5.3 page [2]; Drupal issue referencing CVE-2020-28469 via dependency chain [3]; Socket research on chokidar typosquatting packages [4].


🌐 Web query:

express 4.18.2 security vulnerabilities CVE

💡 Result:

Express 4.18.2 is affected by these published CVEs (directly and via its common bundled dependencies):

Express (core)

  • CVE-2024-29041 – open redirect via malformed URLs in res.location() / res.redirect() (affected < 4.19.2). Fix: upgrade to ≥ 4.19.2. [1]
  • CVE-2024-43796 – XSS / untrusted code execution risk when passing user input to response.redirect() (affected < 4.20.0). Fix: upgrade to ≥ 4.20.0. [2]

Dependencies typically shipped with Express 4.18.x

  • CVE-2024-43799send untrusted input to SendStream.redirect() (patched in send 0.19.0). Express 4.x updates this via later releases. [3]
  • CVE-2024-43800serve-static untrusted input to redirect() (patched in serve-static 1.16.0). Express 4.x updates this via later releases. [4]
  • CVE-2024-45296path-to-regexp ReDoS (Express recommends upgrading the module; later Express 4.x releases update it). [5][6]
  • CVE-2024-47764cookie cookie-field injection via crafted name/path/domain (patched in cookie 0.7.0; Express 4.21.1 updates cookie). [7][6]

Practical remediation

  • If you must stay on Express 4.x, upgrade to the latest 4.x listed on Express’ security updates page (it includes dependency bumps for send, serve-static, cookie, and path-to-regexp). [6]

Sources: [1] [2] [3] [4] [5] [6] [7]


🌐 Web query:

ws 8.14.2 security vulnerabilities CVE

💡 Result:

For the Node.js ws (websockets/ws) library, version 8.14.2 falls in the affected range for:

  • CVE-2024-37890Denial of Service (DoS): a request with too many HTTP headers (exceeding server.maxHeadersCount) can crash a ws server. Affected versions include ws >= 8.0.0 and < 8.17.1, so 8.14.2 is vulnerable. Fix: upgrade to ws 8.17.1+ (or use backports for older major versions). Sources: NVD and vendor advisory-derived databases. [1] [2]

Sources:
[1] https://nvd.nist.gov/vuln/detail/CVE-2024-37890
[2] https://cve.armis.com/cve-2024-37890


🏁 Script executed:

#!/bin/bash
# Check npm registry for current versions and basic info
echo "=== Checking npm registry for package information ==="

for pkg in "chokidar" "express" "ws"; do
  echo ""
  echo "--- $pkg ---"
  npm view "$pkg" --json 2>/dev/null | jq '.version' 2>/dev/null || echo "Could not fetch from npm"
done

Repository: obra/superpowers

Length of output: 185


Upgrade express and ws for security fixes; add "private": true.

The package.json has known security vulnerabilities:

  • express 4.18.2: affected by CVE-2024-29041 (open redirect), CVE-2024-43796 (XSS), and dependency vulnerabilities. Upgrade to ≥ 4.20.0 (or latest 4.x/5.x).
  • ws 8.14.2: affected by CVE-2024-37890 (DoS via excessive HTTP headers). Upgrade to ≥ 8.17.1.
  • chokidar 3.5.3: no known vulnerabilities.

Also add "private": true to prevent accidental npm publish of this internal tool.

🤖 Prompt for AI Agents
In `@lib/brainstorm-server/package.json` around lines 1 - 11, Update package.json
to set "private": true at the top level and bump the dependency versions: change
"express" to a version >= 4.20.0 (or latest 4.x/5.x) and "ws" to >= 8.17.1 in
the "dependencies" object (leave "chokidar" unchanged); after editing
package.json run npm install (or yarn install) to update
package-lock.json/yarn.lock and verify no breaking changes.

Comment on lines 33 to 45
# Wait for server-started message (check log file)
for i in {1..50}; do
if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then
# Extract and output the server-started line
grep "server-started" "$LOG_FILE" | head -1
exit 0
fi
sleep 0.1
done

# Timeout - server didn't start
echo '{"error": "Server failed to start within 5 seconds"}'
exit 1
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clean up background server on startup timeout.
On timeout, the script exits but leaves the background server running and the temp dir behind, which can leak processes/ports across runs.

🔧 Proposed fix
 # Timeout - server didn't start
-echo '{"error": "Server failed to start within 5 seconds"}'
-exit 1
+if kill -0 "$SERVER_PID" 2>/dev/null; then
+  kill "$SERVER_PID" 2>/dev/null
+fi
+rm -f "$PID_FILE"
+rm -rf "$SCREEN_DIR"
+echo '{"error": "Server failed to start within 5 seconds"}'
+exit 1
📝 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.

Suggested change
# Wait for server-started message (check log file)
for i in {1..50}; do
if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then
# Extract and output the server-started line
grep "server-started" "$LOG_FILE" | head -1
exit 0
fi
sleep 0.1
done
# Timeout - server didn't start
echo '{"error": "Server failed to start within 5 seconds"}'
exit 1
# Wait for server-started message (check log file)
for i in {1..50}; do
if grep -q "server-started" "$LOG_FILE" 2>/dev/null; then
# Extract and output the server-started line
grep "server-started" "$LOG_FILE" | head -1
exit 0
fi
sleep 0.1
done
# Timeout - server didn't start
if kill -0 "$SERVER_PID" 2>/dev/null; then
kill "$SERVER_PID" 2>/dev/null
fi
rm -f "$PID_FILE"
rm -rf "$SCREEN_DIR"
echo '{"error": "Server failed to start within 5 seconds"}'
exit 1
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 34-34: i appears unused. Verify use (or export if used externally).

(SC2034)

🤖 Prompt for AI Agents
In `@lib/brainstorm-server/start-server.sh` around lines 33 - 45, The timeout
branch currently exits without cleaning up the background server and temporary
directory; capture the background server PID when launching it (e.g., assign $!
to SERVER_PID where the server is started) and on timeout kill that PID (first
try kill "$SERVER_PID" then fallback to kill -9 if needed), wait for it to exit,
and remove the temp directory (e.g., rm -rf "$TEMP_DIR" or "$TMP_DIR") before
printing the JSON error and exiting with status 1; also handle cases where
SERVER_PID or the temp-dir variable may be empty to avoid errors.

Comment on lines +92 to +98
echo "================================================================================"
cd "$SCRIPT_DIR/../.." && timeout 120 claude -p "$PROMPT" --permission-mode bypassPermissions 2>&1 | tee "$OUTPUT_FILE" || {
echo ""
echo "================================================================================"
echo "EXECUTION FAILED (exit code: $?)"
exit 1
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n tests/claude-code/test-document-review-system.sh | sed -n '85,105p'

Repository: obra/superpowers

Length of output: 984


Capture the failing exit code before running echo statements.
$? on line 96 reflects the exit code of the preceding echo command (which succeeds with 0), not the failed command on line 93. This logs "EXECUTION FAILED (exit code: 0)" regardless of the actual failure status.

🔧 Proposed fix
 cd "$SCRIPT_DIR/../.." && timeout 120 claude -p "$PROMPT" --permission-mode bypassPermissions 2>&1 | tee "$OUTPUT_FILE" || {
+    status=$?
     echo ""
     echo "================================================================================"
-    echo "EXECUTION FAILED (exit code: $?)"
+    echo "EXECUTION FAILED (exit code: $status)"
     exit 1
 }
📝 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.

Suggested change
echo "================================================================================"
cd "$SCRIPT_DIR/../.." && timeout 120 claude -p "$PROMPT" --permission-mode bypassPermissions 2>&1 | tee "$OUTPUT_FILE" || {
echo ""
echo "================================================================================"
echo "EXECUTION FAILED (exit code: $?)"
exit 1
}
echo "================================================================================"
cd "$SCRIPT_DIR/../.." && timeout 120 claude -p "$PROMPT" --permission-mode bypassPermissions 2>&1 | tee "$OUTPUT_FILE" || {
status=$?
echo ""
echo "================================================================================"
echo "EXECUTION FAILED (exit code: $status)"
exit 1
}
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 96-96: This $? refers to echo/printf, not a previous command. Assign to variable to avoid it being overwritten.

(SC2320)

🤖 Prompt for AI Agents
In `@tests/claude-code/test-document-review-system.sh` around lines 92 - 98, The
failing exit code of the pipeline is overwritten by the subsequent echo, so
capture the exit status immediately after running the command and before any
other commands; assign the exit status of the `timeout 120 claude -p "$PROMPT"
--permission-mode bypassPermissions 2>&1 | tee "$OUTPUT_FILE"` pipeline (i.e.,
`$?`) to a local variable (e.g., EXIT_CODE) right after it fails, then use that
variable in the `echo "EXECUTION FAILED (exit code: $EXIT_CODE)"` and the `exit
$EXIT_CODE` calls; reference the pipeline invocation and the `$?` usage in your
fix.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@docs/plans/2026-01-17-visual-brainstorming.md`:
- Around line 37-122: The snippet uses a single SCREEN_FILE and watches it,
which is outdated; update it to use SCREEN_DIR, watch the directory for 'add'
and 'change' events, and replace the simple file-serving logic with a
getNewestScreen() implementation that picks the newest .html by mtime and serves
that file; specifically, remove references to SCREEN_FILE usage, switch to
SCREEN_DIR and SCREEN_DIR env var handling, add a getNewestScreen() helper used
by the express route, and update the chokidar watcher to watch SCREEN_DIR and
handle both additions and changes so behavior matches the current implementation
in getNewestScreen and the directory-watching logic.

In `@lib/brainstorm-server/start-server.sh`:
- Around line 20-25: The cleanup block using PID_FILE derived from SESSION_ID
(in start-server.sh) is ineffective because SESSION_ID is unique per run; change
the cleanup to target a stable PID location or scan for stale PID files: either
set PID_FILE to a fixed path (e.g., /tmp/brainstorm-server.pid) and use that in
the existing removal/killing logic, or implement a scan that iterates
/tmp/brainstorm-*/ directories to read and validate each PID file and kill stale
PIDs before removing them; update references to PID_FILE and SESSION_ID
accordingly and ensure the new logic safely checks process liveness before kill
to avoid false positives.
- Line 28: The script currently runs cd "$SCRIPT_DIR" without checking if it
succeeded, so if the chdir fails the subsequent node index.js runs from the
wrong directory; update the start-up logic to test the exit status of cd (or use
a conditional like cd "$SCRIPT_DIR" || { ... }) and on failure log an error
mentioning SCRIPT_DIR and exit with a non-zero status before invoking node
index.js so the server never starts from the wrong location.

In `@RELEASE-NOTES.md`:
- Around line 26-30: The unreleased changelog contains a duplicate breaking
change entry "OpenCode: Switched to native skills system" that already appears
in the v4.1.0 section; remove the duplicate from the unreleased section (or mark
it explicitly as intentionally carried forward) so the unreleased section only
lists new, unreleased changes; locate the entry by searching for the exact
heading/text "OpenCode: Switched to native skills system" in RELEASE-NOTES.md
and either delete that block under the Unreleased heading or add a clear note
that it’s intentionally repeated.
♻️ Duplicate comments (3)
lib/brainstorm-server/package.json (1)

2-9: Duplicate: verify dependency versions and consider "private": true.

Same as the earlier review note: the versions at Line 6–9 should be verified for current advisories/updates, and adding "private": true would prevent accidental publish.

Are there any published security advisories affecting express 4.18.2 or ws 8.14.2, and what are the latest secure versions in their current major lines?
lib/brainstorm-server/start-server.sh (1)

43-45: Clean up background server on startup timeout.

On timeout, the script exits but leaves the background server running and the temp directory behind. This can leak processes and ports across runs.

🔧 Proposed fix
 # Timeout - server didn't start
+if kill -0 "$SERVER_PID" 2>/dev/null; then
+    kill "$SERVER_PID" 2>/dev/null
+fi
+rm -f "$PID_FILE"
+rm -rf "$SCREEN_DIR"
 echo '{"error": "Server failed to start within 5 seconds"}'
 exit 1
tests/claude-code/test-document-review-system.sh (1)

92-98: Capture the failing exit code before running echo statements.

$? on line 96 reflects the exit code of the preceding echo command (which succeeds with 0), not the failed command on line 93. This logs "EXECUTION FAILED (exit code: 0)" regardless of the actual failure status.

🔧 Proposed fix
-cd "$SCRIPT_DIR/../.." && timeout 120 claude -p "$PROMPT" --permission-mode bypassPermissions 2>&1 | tee "$OUTPUT_FILE" || {
+cd "$SCRIPT_DIR/../.." && timeout 120 claude -p "$PROMPT" --permission-mode bypassPermissions 2>&1 | tee "$OUTPUT_FILE" || {
+    status=$?
     echo ""
     echo "================================================================================"
-    echo "EXECUTION FAILED (exit code: $?)"
+    echo "EXECUTION FAILED (exit code: $status)"
     exit 1
 }
🧹 Nitpick comments (6)
lib/brainstorm-server/stop-server.sh (1)

14-19: Validate path before rm -rf and verify process termination.

The script removes $SCREEN_DIR based on user input without path validation. Consider:

  1. Verifying SCREEN_DIR starts with /tmp/brainstorm- to prevent accidental deletion of unrelated directories
  2. Waiting briefly for the process to terminate before cleanup
🔧 Proposed fix
 if [[ -f "$PID_FILE" ]]; then
   pid=$(cat "$PID_FILE")
+  # Validate SCREEN_DIR is a brainstorm temp directory
+  if [[ ! "$SCREEN_DIR" =~ ^/tmp/brainstorm- ]]; then
+    echo '{"error": "Invalid screen directory path"}'
+    exit 1
+  fi
   kill "$pid" 2>/dev/null
+  # Brief wait for process to terminate
+  sleep 0.1
   # Clean up session directory
   rm -rf "$SCREEN_DIR"
   echo '{"status": "stopped"}'
skills/writing-plans/plan-document-reviewer-prompt.md (1)

40-40: Minor inconsistency with spec reviewer status format.

The status format here (Approved | Issues Found) differs from the spec reviewer template which uses emoji (✅ Approved | ❌ Issues Found). Consider aligning the formats for consistency across reviewer outputs.

🔧 Proposed fix
-    **Status:** Approved | Issues Found
+    **Status:** ✅ Approved | ❌ Issues Found
tests/claude-code/test-document-review-system.sh (1)

141-150: Test 4 logic has ambiguous success path.

Lines 148-149 report [PASS] with message "ambiguous format but found issues" when neither the explicit rejection patterns (Issues Found, , not approved) nor the explicit approval patterns (Approved, without issues) are matched. This could mask cases where the reviewer output is malformed or doesn't follow the expected template at all.

Consider tightening the verification to require explicit issue detection rather than falling through to a pass:

🔧 Proposed fix
 # Test 4: Reviewer did NOT approve (found issues)
 echo "Test 4: Reviewer verdict..."
 if grep -qi "Issues Found\|❌\|not approved\|issues found" "$OUTPUT_FILE"; then
     echo "  [PASS] Reviewer correctly found issues (not approved)"
 elif grep -qi "Approved\|✅" "$OUTPUT_FILE" && ! grep -qi "Issues Found\|❌" "$OUTPUT_FILE"; then
     echo "  [FAIL] Reviewer incorrectly approved spec with errors"
     FAILED=$((FAILED + 1))
 else
-    echo "  [PASS] Reviewer identified problems (ambiguous format but found issues)"
+    echo "  [WARN] Reviewer output format unclear - manual verification needed"
+    echo "         Neither explicit approval nor explicit rejection found"
+    # Don't fail, but flag for attention
 fi
docs/superpowers/plans/2026-01-22-document-review-system.md (1)

26-75: Verify the nested code block structure in the template.

The embedded template contains a markdown code fence (lines 26-35) followed by the actual prompt content (lines 36-71) followed by another code fence marker (line 72). This nested structure might be confusing when implementing.

Consider clarifying whether:

  • The entire content (lines 27-74) should be written to spec-document-reviewer-prompt.md as-is
  • The Task tool prompt is the section from lines 38-71

The current structure mixes the template documentation with the actual prompt content, which could lead to implementation errors.

💡 Suggested clarification

Consider restructuring to clearly separate:

  1. What the markdown file should contain (the template)
  2. What the Task tool prompt should be (extracted from the template)

For example:

- [ ] **Step 1:** Create the reviewer prompt template file

The file should contain:
- Purpose and dispatch timing
- A template showing how to use the Task tool
- The actual prompt text to send to the reviewer subagent

Create `skills/brainstorming/spec-document-reviewer-prompt.md` with this content:

```markdown
[template content here]
</details>

</blockquote></details>
<details>
<summary>docs/plans/2026-01-17-visual-brainstorming.md (2)</summary><blockquote>

`545-545`: **Add language specifier to code fence.**

The code fence at line 545 (showing .gitignore content) should specify a language for better syntax highlighting and markdown compliance.



<details>
<summary>💡 Suggested fix</summary>

```diff
-```
+```gitignore
 lib/brainstorm-server/node_modules/
</details>

---

`1-11`: **Update the execution requirement header to match the newer pattern.**

Line 3 uses "REQUIRED SUB-SKILL: Use superpowers:executing-plans" which is inconsistent with the newer pattern established in the document-review-system plan. Consider updating to the more flexible pattern:

```markdown
> **For Claude:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan.

This newer pattern supports both execution modes and is already adopted in the document-review-system plan (2026-01-22).

Comment on lines +20 to +25
# Kill any existing server
if [[ -f "$PID_FILE" ]]; then
old_pid=$(cat "$PID_FILE")
kill "$old_pid" 2>/dev/null
rm -f "$PID_FILE"
fi
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Old server cleanup logic is ineffective.

This block attempts to kill any existing server, but PID_FILE is derived from SESSION_ID which is unique per invocation ($$-$(date +%s)). The PID file will never exist for a new session, so this cleanup is a no-op. If the intent is to clean up orphaned servers from crashed sessions, consider either:

  1. Using a fixed PID file location (e.g., /tmp/brainstorm-server.pid)
  2. Or scanning for any /tmp/brainstorm-*/ directories with stale PID files
🤖 Prompt for AI Agents
In `@lib/brainstorm-server/start-server.sh` around lines 20 - 25, The cleanup
block using PID_FILE derived from SESSION_ID (in start-server.sh) is ineffective
because SESSION_ID is unique per run; change the cleanup to target a stable PID
location or scan for stale PID files: either set PID_FILE to a fixed path (e.g.,
/tmp/brainstorm-server.pid) and use that in the existing removal/killing logic,
or implement a scan that iterates /tmp/brainstorm-*/ directories to read and
validate each PID file and kill stale PIDs before removing them; update
references to PID_FILE and SESSION_ID accordingly and ensure the new logic
safely checks process liveness before kill to avoid false positives.

fi

# Start server, capturing output to log file
cd "$SCRIPT_DIR"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Handle cd failure.

If cd "$SCRIPT_DIR" fails, the script continues and runs node index.js from the wrong directory.

🔧 Proposed fix
-cd "$SCRIPT_DIR"
+cd "$SCRIPT_DIR" || { echo '{"error": "Failed to change to script directory"}'; exit 1; }
📝 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.

Suggested change
cd "$SCRIPT_DIR"
cd "$SCRIPT_DIR" || { echo '{"error": "Failed to change to script directory"}'; exit 1; }
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 28-28: Use 'cd ... || exit' or 'cd ... || return' in case cd fails.

(SC2164)

🤖 Prompt for AI Agents
In `@lib/brainstorm-server/start-server.sh` at line 28, The script currently runs
cd "$SCRIPT_DIR" without checking if it succeeded, so if the chdir fails the
subsequent node index.js runs from the wrong directory; update the start-up
logic to test the exit status of cd (or use a conditional like cd "$SCRIPT_DIR"
|| { ... }) and on failure log an error mentioning SCRIPT_DIR and exit with a
non-zero status before invoking node index.js so the server never starts from
the wrong location.

Comment on lines +26 to +30
**OpenCode: Switched to native skills system**

Superpowers for OpenCode now uses OpenCode's native `skill` tool instead of custom `use_skill`/`find_skills` tools. This is a cleaner integration that works with OpenCode's built-in skill discovery.

**Migration required:** Skills must be symlinked to `~/.config/opencode/skills/superpowers/` (see updated installation docs).
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Duplicate breaking change entry with v4.1.0.

The "OpenCode: Switched to native skills system" breaking change (lines 26-30) duplicates content from v4.1.0 (lines 105-109). If this is intentionally carried forward to unreleased for emphasis, consider removing it since it's already released. Otherwise, if v4.1.0 changes were already deployed, the unreleased section should only contain new changes.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

26-26: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🤖 Prompt for AI Agents
In `@RELEASE-NOTES.md` around lines 26 - 30, The unreleased changelog contains a
duplicate breaking change entry "OpenCode: Switched to native skills system"
that already appears in the v4.1.0 section; remove the duplicate from the
unreleased section (or mark it explicitly as intentionally carried forward) so
the unreleased section only lists new, unreleased changes; locate the entry by
searching for the exact heading/text "OpenCode: Switched to native skills
system" in RELEASE-NOTES.md and either delete that block under the Unreleased
heading or add a clear note that it’s intentionally repeated.

@obra obra added claude-code Claude Code (Anthropic CLI) issues enhancement New feature or request labels Feb 5, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@lib/brainstorm-server/helper.js`:
- Around line 120-129: The send() function is building a payload object with
choice and feedback but calls sendToClaude(payload) while sendToClaude(event)
expects feedback as a string and assigns event.feedback directly; update
sendToClaude to accept a structured object (e.g., { choice, feedback }) and map
its fields to the event sent (or alternatively change send() to call
sendToClaude(feedback) and send choice separately). Locate the functions
window.send and sendToClaude and modify sendToClaude’s parameter handling so it
reads payload.feedback and payload.choice (and sets event.feedback and
event.choice) to preserve both fields.

In `@lib/brainstorm-server/index.js`:
- Around line 35-46: getNewestScreen reads SCREEN_DIR then calls fs.statSync for
each file which can throw if a file is removed (TOCTOU); wrap the stat call in a
try/catch inside getNewestScreen (or use fs.statSync guarded by fs.existsSync)
and skip files that throw or can't be stat'ed so the function returns the newest
existing .html file instead of crashing; update the mapping block that builds
the {name,path,mtime} entries to catch stat errors and filter out failed entries
before sorting.
🧹 Nitpick comments (2)
skills/brainstorming/SKILL.md (1)

41-45: Clarify the spec-document-reviewer-prompt.md location.

The Spec Review Loop references spec-document-reviewer-prompt.md without specifying its location. For consistency with line 71's visual-companion reference, consider using the full path.

📝 Proposed clarification
 **Spec Review Loop:**
 After writing the spec document:
-1. Dispatch spec-document-reviewer subagent (see spec-document-reviewer-prompt.md)
+1. Dispatch spec-document-reviewer subagent (see `${CLAUDE_PLUGIN_ROOT}/skills/brainstorming/spec-document-reviewer-prompt.md`)
 2. If Issues Found: fix, re-dispatch, repeat until Approved
 3. If loop exceeds 5 iterations, surface to human for guidance
lib/brainstorm-server/index.js (1)

27-32: Regex-based template injection is fragile.

The wrapInFrame function uses a regex to find and replace content within the frame template. If the template structure changes (e.g., different whitespace, additional wrapper divs), the regex may fail silently and return malformed HTML.

Consider using a more robust approach like a placeholder marker:

💡 Proposed improvement

In frame-template.html, add a placeholder comment:

<div id="claude-content">
  <!-- CONTENT_PLACEHOLDER -->
</div>

Then in index.js:

 function wrapInFrame(content) {
-  return frameTemplate.replace(
-    /(<div id="claude-content">)[\s\S]*?(<\/div>\s*<\/div>\s*<div class="feedback-footer">)/,
-    `$1\n      ${content}\n    $2`
-  );
+  return frameTemplate.replace('<!-- CONTENT_PLACEHOLDER -->', content);
 }

This is more maintainable and won't break if the template structure evolves.

Comment on lines 120 to 129
window.send = function() {
const feedbackEl = document.getElementById('feedback');
const feedback = feedbackEl ? feedbackEl.value.trim() : '';
const payload = {};
if (window.selectedChoice) payload.choice = window.selectedChoice;
if (feedback) payload.feedback = feedback;
if (Object.keys(payload).length === 0) return;
sendToClaude(payload);
if (feedbackEl) feedbackEl.value = '';
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Parameter mismatch in sendToClaude call.

The send() function passes an object { choice, feedback } to sendToClaude() on line 127, but sendToClaude() (lines 92-96) expects a simple feedback parameter and assigns it directly to event.feedback. This means the choice is being sent as the feedback property, losing the structured data.

🔧 Proposed fix
   window.send = function() {
     const feedbackEl = document.getElementById('feedback');
     const feedback = feedbackEl ? feedbackEl.value.trim() : '';
     const payload = {};
     if (window.selectedChoice) payload.choice = window.selectedChoice;
     if (feedback) payload.feedback = feedback;
     if (Object.keys(payload).length === 0) return;
-    sendToClaude(payload);
+    sendEvent({
+      type: 'send-to-claude',
+      choice: payload.choice || null,
+      feedback: payload.feedback || null
+    });
+    // Show themed confirmation page
+    document.body.innerHTML = `
+      <div style="display: flex; align-items: center; justify-content: center; height: 100vh; font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif; background: var(--bg-primary, `#f5f5f7`);">
+        <div style="text-align: center; color: var(--text-secondary, `#86868b`);">
+          <h2 style="color: var(--text-primary, `#1d1d1f`); margin-bottom: 0.5rem;">Sent to Claude</h2>
+          <p>Return to the terminal to see Claude's response.</p>
+        </div>
+      </div>
+    `;
     if (feedbackEl) feedbackEl.value = '';
   };

Alternatively, update sendToClaude to accept an object:

-  function sendToClaude(feedback) {
+  function sendToClaude(payload) {
     sendEvent({
       type: 'send-to-claude',
-      feedback: feedback || null
+      choice: payload?.choice || null,
+      feedback: payload?.feedback || null
     });
📝 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.

Suggested change
window.send = function() {
const feedbackEl = document.getElementById('feedback');
const feedback = feedbackEl ? feedbackEl.value.trim() : '';
const payload = {};
if (window.selectedChoice) payload.choice = window.selectedChoice;
if (feedback) payload.feedback = feedback;
if (Object.keys(payload).length === 0) return;
sendToClaude(payload);
if (feedbackEl) feedbackEl.value = '';
};
window.send = function() {
const feedbackEl = document.getElementById('feedback');
const feedback = feedbackEl ? feedbackEl.value.trim() : '';
const payload = {};
if (window.selectedChoice) payload.choice = window.selectedChoice;
if (feedback) payload.feedback = feedback;
if (Object.keys(payload).length === 0) return;
sendEvent({
type: 'send-to-claude',
choice: payload.choice || null,
feedback: payload.feedback || null
});
// Show themed confirmation page
document.body.innerHTML = `
<div style="display: flex; align-items: center; justify-content: center; height: 100vh; font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif; background: var(--bg-primary, `#f5f5f7`);">
<div style="text-align: center; color: var(--text-secondary, `#86868b`);">
<h2 style="color: var(--text-primary, `#1d1d1f`); margin-bottom: 0.5rem;">Sent to Claude</h2>
<p>Return to the terminal to see Claude's response.</p>
</div>
</div>
`;
if (feedbackEl) feedbackEl.value = '';
};
🤖 Prompt for AI Agents
In `@lib/brainstorm-server/helper.js` around lines 120 - 129, The send() function
is building a payload object with choice and feedback but calls
sendToClaude(payload) while sendToClaude(event) expects feedback as a string and
assigns event.feedback directly; update sendToClaude to accept a structured
object (e.g., { choice, feedback }) and map its fields to the event sent (or
alternatively change send() to call sendToClaude(feedback) and send choice
separately). Locate the functions window.send and sendToClaude and modify
sendToClaude’s parameter handling so it reads payload.feedback and
payload.choice (and sets event.feedback and event.choice) to preserve both
fields.

Comment on lines +35 to +46
function getNewestScreen() {
const files = fs.readdirSync(SCREEN_DIR)
.filter(f => f.endsWith('.html'))
.map(f => ({
name: f,
path: path.join(SCREEN_DIR, f),
mtime: fs.statSync(path.join(SCREEN_DIR, f)).mtime.getTime()
}))
.sort((a, b) => b.mtime - a.mtime);

return files.length > 0 ? files[0].path : null;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential TOCTOU race in getNewestScreen.

The function reads the directory, stats each file, and sorts by mtime. If a file is deleted between readdirSync and statSync, this will throw. Consider wrapping the stat call in a try/catch.

🛡️ Proposed fix
 function getNewestScreen() {
   const files = fs.readdirSync(SCREEN_DIR)
     .filter(f => f.endsWith('.html'))
-    .map(f => ({
-      name: f,
-      path: path.join(SCREEN_DIR, f),
-      mtime: fs.statSync(path.join(SCREEN_DIR, f)).mtime.getTime()
-    }))
+    .map(f => {
+      const filePath = path.join(SCREEN_DIR, f);
+      try {
+        return {
+          name: f,
+          path: filePath,
+          mtime: fs.statSync(filePath).mtime.getTime()
+        };
+      } catch {
+        return null;
+      }
+    })
+    .filter(Boolean)
     .sort((a, b) => b.mtime - a.mtime);

   return files.length > 0 ? files[0].path : null;
 }
🤖 Prompt for AI Agents
In `@lib/brainstorm-server/index.js` around lines 35 - 46, getNewestScreen reads
SCREEN_DIR then calls fs.statSync for each file which can throw if a file is
removed (TOCTOU); wrap the stat call in a try/catch inside getNewestScreen (or
use fs.statSync guarded by fs.existsSync) and skip files that throw or can't be
stat'ed so the function returns the newest existing .html file instead of
crashing; update the mapping block that builds the {name,path,mtime} entries to
catch stat errors and filter out failed entries before sorting.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@RELEASE-NOTES.md`:
- Around line 3-91: Release notes omission: add a new Unreleased subsection
documenting the "document review system and workflow enforcement" feature,
describing the spec and plan reviewer prompts and review loops; specifically
mention the new prompt files
skills/brainstorming/spec-document-reviewer-prompt.md and
skills/writing-plans/plan-document-reviewer-prompt.md, the addition of review
loops to the brainstorming and writing-plans skills, and the integration test
tests/claude-code/test-document-review-system.sh; keep the entry concise, mirror
the style of other entries (heading, short bullets), and include a one-line
migration/usage note indicating where reviewers live and how they are invoked.
🧹 Nitpick comments (4)
RELEASE-NOTES.md (1)

62-62: Remove trailing space inside code span.

The code span contains a trailing space: `bash `. The space should be outside the backticks for proper formatting.

✨ Proposed fix
-Claude Code 2.1.x changed how hooks execute on Windows: it now auto-detects `.sh` files in commands and prepends `bash `. This broke the polyglot wrapper pattern because `bash "run-hook.cmd" session-start.sh` tries to execute the .cmd file as a bash script.
+Claude Code 2.1.x changed how hooks execute on Windows: it now auto-detects `.sh` files in commands and prepends `bash`. This broke the polyglot wrapper pattern because `bash "run-hook.cmd" session-start.sh` tries to execute the .cmd file as a bash script.

As per coding guidelines, markdownlint-cli2 flagged MD038 (no-space-in-code).

skills/writing-plans/plan-document-reviewer-prompt.md (1)

40-40: Minor inconsistency with status format.

The output format shows Approved | Issues Found without emoji indicators, while the implementation plan at docs/superpowers/plans/2026-01-22-document-review-system.md (lines 64, 184) uses ✅ Approved | ❌ Issues Found. Consider aligning for consistency.

📝 Suggested change
-    **Status:** Approved | Issues Found
+    **Status:** ✅ Approved | ❌ Issues Found
lib/brainstorm-server/helper.js (1)

9-11: Address linting warning for forEach callback return value.

The arrow function implicitly returns the result of ws.send(), which is flagged by static analysis. While functionally harmless, using block syntax clarifies intent.

📝 Suggested change
     ws.onopen = () => {
-      eventQueue.forEach(e => ws.send(JSON.stringify(e)));
+      eventQueue.forEach(e => { ws.send(JSON.stringify(e)); });
       eventQueue = [];
     };
tests/brainstorm-server/server.test.js (1)

10-10: Hardcoded test directory may conflict with parallel test runs.

TEST_DIR is hardcoded to /tmp/brainstorm-test. If tests run in parallel (e.g., CI matrix), they could interfere with each other. Consider using a unique suffix or mkdtemp.

📝 Suggested change
-const TEST_DIR = '/tmp/brainstorm-test';
+const TEST_DIR = `/tmp/brainstorm-test-${process.pid}`;

Comment on lines +3 to +91
## Unreleased

### Breaking Changes

**Specs and plans directory restructured**

- Specs (brainstorming output) now go to `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md`
- Plans (writing-plans output) now go to `docs/superpowers/plans/YYYY-MM-DD-<feature-name>.md`
- User preferences for spec/plan locations override these defaults
- Migration: move existing files from `docs/plans/` to new locations if desired

**Brainstorming → writing-plans transition enforced**

- After design approval, brainstorming now requires using writing-plans skill
- Platform planning features (e.g., EnterPlanMode) should not be used
- Direct implementation without writing-plans is not allowed

**Subagent-driven development now mandatory on capable harnesses**

- On harnesses with subagent support (Claude Code), subagent-driven-development is now required after plan approval
- No longer offers a choice between subagent-driven and executing-plans
- Executing-plans is only used on harnesses without subagent capability

**OpenCode: Switched to native skills system**

Superpowers for OpenCode now uses OpenCode's native `skill` tool instead of custom `use_skill`/`find_skills` tools. This is a cleaner integration that works with OpenCode's built-in skill discovery.

**Migration required:** Skills must be symlinked to `~/.config/opencode/skills/superpowers/` (see updated installation docs).

### Fixes

**OpenCode: Fixed agent reset on session start (#226)**

The previous bootstrap injection method using `session.prompt({ noReply: true })` caused OpenCode to reset the selected agent to "build" on first message. Now uses `experimental.chat.system.transform` hook which modifies the system prompt directly without side effects.

**OpenCode: Fixed Windows installation (#232)**

- Removed dependency on `skills-core.js` (eliminates broken relative imports when file is copied instead of symlinked)
- Added comprehensive Windows installation docs for cmd.exe, PowerShell, and Git Bash
- Documented proper symlink vs junction usage for each platform

### New Features

**Visual companion for brainstorming skill**

Added optional browser-based visual companion for brainstorming sessions. When users have a browser available, brainstorming can display interactive screens showing current phase, questions, and design decisions in a more readable format than terminal output.

Components:
- `lib/brainstorm-server/` - WebSocket server for real-time updates
- `skills/brainstorming/visual-companion.md` - Integration guide
- Helper scripts for session management with proper isolation
- Browser helper library for event capture

The visual companion is opt-in and falls back gracefully to terminal-only operation.

### Bug Fixes

**Fixed Windows hook execution for Claude Code 2.1.x**

Claude Code 2.1.x changed how hooks execute on Windows: it now auto-detects `.sh` files in commands and prepends `bash `. This broke the polyglot wrapper pattern because `bash "run-hook.cmd" session-start.sh` tries to execute the .cmd file as a bash script.

Fix: hooks.json now calls session-start.sh directly. Claude Code 2.1.x handles the bash invocation automatically. Also added .gitattributes to enforce LF line endings for shell scripts (fixes CRLF issues on Windows checkout).

**Brainstorming visual companion: reduced token cost and improved persistence**

The visual companion now generates much smaller HTML per screen. The server automatically wraps bare content fragments in the frame template (header, CSS theme, feedback footer, interactive JS), so Claude writes only the content portion (~30 lines instead of ~260). Full HTML documents are still served as-is when Claude needs complete control.

Other improvements:
- `toggleSelect`/`send`/`selectedChoice` moved from inline template script to `helper.js` (auto-injected)
- `start-server.sh --project-dir` persists mockups under `.superpowers/brainstorm/` instead of `/tmp`
- `stop-server.sh` only deletes ephemeral `/tmp` sessions, preserving persistent ones
- Dark mode fix: `sendToClaude` confirmation page now uses CSS variables instead of hardcoded colors
- Skill restructured: SKILL.md is minimal (prompt + pointer); all visual companion details in progressive disclosure doc (`visual-companion.md`)
- Prompt to user now notes the feature is new, token-intensive, and can be slow
- Deleted redundant `CLAUDE-INSTRUCTIONS.md` (content folded into `visual-companion.md`)
- Test fixes: correct env var (`BRAINSTORM_DIR`), polling-based startup wait, new tests for frame wrapping

### Improvements

**Instruction priority clarified in using-superpowers**

Added explicit instruction priority hierarchy to prevent conflicts with user preferences:

1. User's explicit instructions (CLAUDE.md, direct requests) — highest priority
2. Superpowers skills — override default system behavior where they conflict
3. Default system prompt — lowest priority

This ensures users remain in control. If CLAUDE.md says "don't use TDD" and a skill says "always use TDD," CLAUDE.md wins.

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Main PR feature missing from release notes.

The PR title is "feat: document review system and workflow enforcement" and the PR objectives describe a document review system with spec and plan reviewers as the primary feature, but the Unreleased section does not document this feature at all.

According to PR objectives, the following should be documented:

  • Document review system with spec and plan reviewer prompts
  • skills/brainstorming/spec-document-reviewer-prompt.md
  • skills/writing-plans/plan-document-reviewer-prompt.md
  • Review loops added to brainstorming and writing-plans skills
  • Integration test tests/claude-code/test-document-review-system.sh

The release notes only mention directory restructuring, workflow enforcement, and the visual companion. Users reading these notes won't understand the core functionality this PR adds.

🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 62-62: Spaces inside code span elements

(MD038, no-space-in-code)

🤖 Prompt for AI Agents
In `@RELEASE-NOTES.md` around lines 3 - 91, Release notes omission: add a new
Unreleased subsection documenting the "document review system and workflow
enforcement" feature, describing the spec and plan reviewer prompts and review
loops; specifically mention the new prompt files
skills/brainstorming/spec-document-reviewer-prompt.md and
skills/writing-plans/plan-document-reviewer-prompt.md, the addition of review
loops to the brainstorming and writing-plans skills, and the integration test
tests/claude-code/test-document-review-system.sh; keep the entry concise, mirror
the style of other entries (heading, short bullets), and include a one-line
migration/usage note indicating where reviewers live and how they are invoked.

obra and others added 20 commits February 19, 2026 09:35
Create the initial server for the visual brainstorming companion:
- Express server with WebSocket support for browser communication
- File watcher (chokidar) to detect screen.html changes
- Auto-injects helper.js into served HTML for event capture
- Binds to localhost only (127.0.0.1) for security
- Outputs JSON events to stdout for Claude consumption
The spread operator order was causing incoming event types to overwrite
the user-event type marker.
- Add sendToClaude() function to browser helper that shows confirmation
- Add wait-for-event.sh script for watching server output (tail -f | grep -m 1)
- Enables clean event-driven loop: background bash waits for event, completion triggers Claude's turn
Adds browser-based mockup display to replace ASCII art during
brainstorming sessions. Key components:

- Frame template with OS-aware light/dark theming
- CSS helpers for options, cards, mockups, split views
- Server lifecycle scripts (start/stop with random high port)
- Event watcher using tail+grep for feedback loop
- Claude instructions for using the visual companion

The skill now asks users if they want browser mockups and only
runs in Claude Code environments.
- Each session gets unique temp directory (/tmp/brainstorm-{pid}-{timestamp})
- Server outputs screen_dir and screen_file in startup JSON
- stop-server.sh takes screen_dir arg and cleans up session directory
- Document blocking TaskOutput pattern: 10-min timeouts, retry up to 3x,
  then prompt user "let me know when you want to continue"
- New show-and-wait.sh combines write + wait into one command
- Uses polling instead of tail -f (which hangs on macOS)
- Docs updated: start watcher BEFORE writing screen to avoid race
- Reduces terminal noise by consolidating operations
Scripts:
- Rename show-and-wait.sh -> wait-for-feedback.sh (just waits, no HTML piping)
- Remove wait-for-event.sh (used hanging tail -f)
- Workflow now: Write tool for HTML, wait-for-feedback.sh to block

Documentation rewrite:
- Broader "when to use" (UI, architecture, complex choices, spatial)
- Always ask user first before starting
- Scale fidelity to the question being asked
- Explain the question on each page
- Iterate before moving on - validate changes address feedback
- Use real content (Unsplash images) when it matters
- Never use cat/heredoc for HTML (dumps noise into terminal)
- Read screen_file first before Write tool to avoid errors
- Remind user of URL on every step, not just first
- Give text summary of what's on screen before they look
Server now watches directory for new .html files instead of a single
screen file. Claude writes to semantically named files like
platform.html, style.html, layout.html - each screen is a new file.

Benefits:
- No need to read before write (files are always new)
- Semantic filenames describe what's on screen
- History preserved in directory for debugging
- Server serves newest file by mtime automatically

Updated: index.js, start-server.sh, and all documentation.
Clarifies that user instructions (CLAUDE.md, direct requests) always
take precedence over Superpowers skills, which in turn override
default system prompt behavior. Ensures users remain in control.

Also updates RELEASE-NOTES.md with unreleased changes including
the visual companion feature.
* fix use_skill agent context (#290)

* fix: respect OPENCODE_CONFIG_DIR for personal skills lookup (#297)

* fix: respect OPENCODE_CONFIG_DIR for personal skills lookup

The plugin was hardcoded to look for personal skills in ~/.config/opencode/skills,
ignoring users who set OPENCODE_CONFIG_DIR to a custom path (e.g., for dotfiles management).

Now uses OPENCODE_CONFIG_DIR if set, falling back to the default path.

* fix: update help text to use dynamic paths

Use configDir and personalSkillsDir variables in help text so paths
are accurate when OPENCODE_CONFIG_DIR is set.

* fix: normalize OPENCODE_CONFIG_DIR before use

Handle edge cases where the env var might be:
- Empty or whitespace-only
- Using ~ for home directory (common in .env files)
- A relative path

Now trims, expands ~, and resolves to absolute path.

* feat(opencode): use native skills and fix agent reset bug (#226)

- Replace custom use_skill/find_skills tools with OpenCode's native skill tool
- Use experimental.chat.system.transform hook instead of session.prompt
  (fixes #226 agent reset on first message)
- Symlink skills directory into ~/.config/opencode/skills/superpowers/
- Update installation docs with comprehensive Windows support:
  - Command Prompt, PowerShell, and Git Bash instructions
  - Proper symlink vs junction handling
  - Reinstall safety with cleanup steps
  - Verification commands for each shell

* Add OpenCode native skills changes to release notes

Documents:
- Breaking change: switch to native skill tool
- Fix for agent reset bug (#226)
- Fix for Windows installation (#232)

---------

Co-authored-by: Vinicius da Motta <viniciusmotta8@gmail.com>
Co-authored-by: oribi <oribarilan@gmail.com>
* fix: convert shell scripts from CRLF to LF line endings

Add .gitattributes to enforce LF line endings for shell scripts,
preventing bash errors like "/usr/bin/bash: line 1: : command not found"
when scripts are checked out on Windows with CRLF.

Fixes #317 (SessionStart hook fails due to CRLF line endings)

Files converted:
- hooks/session-start.sh
- lib/brainstorm-server/start-server.sh
- lib/brainstorm-server/stop-server.sh
- lib/brainstorm-server/wait-for-feedback.sh
- skills/systematic-debugging/find-polluter.sh

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: update Windows hook execution for Claude Code 2.1.x

Claude Code 2.1.x changed the Windows execution model: it now auto-detects
.sh files in hook commands and prepends "bash " automatically. This broke
the polyglot wrapper because:

  Before: "run-hook.cmd" session-start.sh  (wrapper executes)
  After:  bash "run-hook.cmd" session-start.sh  (bash can't run .cmd)

Changes:
- hooks.json now calls session-start.sh directly (Claude Code handles bash)
- Added deprecation comment to run-hook.cmd explaining the change
- Updated RELEASE-NOTES.md

Fixes #317, #313, #275, #292

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Specs (brainstorming output) now go to docs/superpowers/specs/
- Plans (writing-plans output) now go to docs/superpowers/plans/
- User preferences for locations override these defaults
- Update all skill references and test files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Make writing-plans REQUIRED after design approval
- Explicitly forbid platform planning features (EnterPlanMode, etc.)
- Forbid direct implementation without writing-plans skill

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
obra and others added 30 commits February 19, 2026 09:35
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Spec: docs/superpowers/specs/2026-01-22-document-review-system-design.md
- Plan: docs/superpowers/plans/2026-01-22-document-review-system.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The `- [ ] ### Task N:` syntax was unusual and might not render
correctly in all markdown parsers. Now only steps have checkboxes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests verify:
- Spec document reviewer checks (completeness, TODOs)
- Plan document reviewer checks (spec alignment, task decomposition)
- Review loops exist in brainstorming and writing-plans skills
- Chunk-by-chunk review for plans with 1000-line limit
- Iteration guidance (5 iterations, escalate to human)
- Checkbox syntax on steps only (not task headings)
- Correct directories (docs/superpowers/specs, docs/superpowers/plans)
- Reviewers are advisory
- Same agent fixes issues (preserves context)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Creates test project with spec containing intentional errors
- Runs Claude to actually review using spec-document-reviewer template
- Verifies reviewer catches TODO and "specified later" deferrals
- Checks review format and verdict

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move toggleSelect/send/selectedChoice from frame-template.html inline
script to helper.js so they're auto-injected. Server now detects bare
HTML fragments (no DOCTYPE/html tag) and wraps them in the frame
template automatically. Full documents pass through as before.

Fix dark mode in sendToClaude confirmation (was using hardcoded colors).
Fix test env var bug (BRAINSTORM_SCREEN -> BRAINSTORM_DIR).
Add tests for fragment wrapping, full doc passthrough, and helper.js.
start-server.sh now accepts --project-dir to store session files under
.superpowers/brainstorm/ instead of /tmp. stop-server.sh only deletes
ephemeral /tmp sessions, keeping persistent ones for later review.

Fix test race condition with polling-based server startup wait.
SKILL.md is now minimal: process, principles, and a prompt that notes
the visual companion is new/token-intensive/slow. All visual companion
details move to visual-companion.md as a progressive disclosure document
read only when the user opts in.

Delete CLAUDE-INSTRUCTIONS.md (content folded into visual-companion.md).
Document fragment vs full-document behavior and --project-dir persistence.
…ills

Brainstorming: design-for-isolation guidance and brownfield codebase awareness
Writing-plans: file structure section requiring decomposition before task definition
Implementer prompt: code organization awareness, structured escalation protocol
  (DONE/DONE_WITH_CONCERNS/BLOCKED/NEEDS_CONTEXT), explicit permission to stop
Subagent-driven-development: provider-agnostic model selection tiers, escalation handling
- Define DONE_WITH_CONCERNS handling in SDD controller flow
- Make implementer action explicit when file grows beyond plan intent
- Reword writing-plans file size reasoning (avoid tooling-artifact language)
- Add decomposition awareness to code quality reviewer prompt
Focus on whether this implementation grew or created large files,
not pre-existing file sizes in brownfield codebases.
Spec reviewer now checks for unit decomposition with clear boundaries.
Plan reviewer now checks file structure and whether files will grow
too large to reason about.
Brainstorming now assesses whether a project is too large for a single
spec and helps decompose into sub-projects. Spec reviewer checks scope.
Writing-plans has a backstop if brainstorming missed it.
Testing showed the model skipped scope assessment when it was a
separate step after "Understanding the idea." Inlining it as the
first thing in understanding ensures it fires before detailed questions.
Claude Code spawns hook commands with shell:true + windowsHide:true,
but on Windows the execution chain cmd.exe -> bash.exe causes Git
Bash (MSYS2) to allocate its own console window, bypassing the hide
flag. This creates visible terminal windows that steal focus on every
SessionStart event (startup, resume, clear, compact).

The fix:
- Rename session-start.sh to session-start (no extension) so Claude
  Code's .sh auto-detection regex doesn't fire and prepend "bash"
- Restore run-hook.cmd polyglot wrapper to control bash invocation
  on Windows (tries known Git Bash paths, then PATH, then exits
  silently if no bash found)
- On Unix, the polyglot's shell portion runs the script directly

This avoids Claude Code's broken .sh auto-prepend, gives us control
over how bash is invoked on Windows, and gracefully handles missing
bash instead of erroring.

Addresses: #440, #414, #354, #417, #293
Upstream: anthropics/claude-code#14828
…ing table

Codex subagents inherit filesystem access and can discover superpowers skills
via native discovery. Without guidance, they activate the 1% rule and invoke
full skill workflows instead of executing their assigned task.

- Add SUBAGENT-STOP gate check above the 1% rule in using-superpowers
- Add Codex dispatch routing table (spawn_agent/wait/close_agent)
- Add scope directives to all 4 subagent dispatch templates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace verbose scope explanations with a direct override statement
that explicitly claims priority over prior guidance (i.e. the
using-superpowers 1% rule injected by Codex's skill discovery).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Addresses Jesse's review feedback on PR #450:
- Move inline routing table from using-superpowers to references/codex-tools.md,
  leveraging Codex's native progressive disclosure for companion files
- Narrow SUBAGENT-STOP from "Do not invoke skills" to "Do not invoke
  superpowers skills" so subagents can still use non-superpowers skills

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Let the SUBAGENT-STOP gate in using-superpowers handle skill leakage
instead of per-template directives. This avoids blocking non-superpowers
skills that users may want subagents to use.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop 'top-level sessions only' which may cause the model to treat
using-superpowers as a first-turn-only skill, breaking skill chaining
on follow-up turns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
)

* Refactor visual brainstorming: browser displays, terminal commands

Replaces the blocking TaskOutput/wait-for-feedback.sh pattern with a
non-blocking model where the browser is an interactive display and the
terminal stays available for conversation.

Server changes:
- Write user click events to .events JSONL file (per-screen, cleared on
  new screen push) so Claude reads them on its next turn
- Replace regex-based wrapInFrame with <!-- CONTENT --> placeholder
- Add --foreground flag to start-server.sh for sandbox environments
- Harden startup with nohup/disown and liveness check

UI changes:
- Remove feedback footer (textarea + Send button)
- Add selection indicator bar ("Option X selected — return to terminal")
- Narrow click handler to [data-choice] elements only

Skill changes:
- Rewrite visual-companion.md for non-blocking loop
- Fix visual companion being skipped on Codex (no browser tools needed)
- Make visual companion offer a standalone question (one question rule)

Deletes wait-for-feedback.sh entirely.

* Add visual companion offer to brainstorming checklist for UX topics

The visual companion was a disconnected section at the bottom of SKILL.md
that agents never reached because it wasn't in the mandatory checklist.
Now step 2 evaluates whether the topic involves visual/UX decisions and
offers the companion if so. Non-visual topics (APIs, data models, etc.)
skip the step entirely.

* Add multi-select support to visual companion

Containers with data-multiselect allow toggling multiple selections.
Without it, behavior is unchanged (single-select). Indicator bar shows
count when multiple items are selected.
Targeted edits to original skill (not a rewrite): scaling paragraph in
Overview, hard gate reframed around confirmed understanding, anti-pattern
refocused on understanding vs ceremony, scalable checklist with GATE
wording, graphviz with decision diamonds at gates, and design doc marked
as 'when warranted'. CSO fix removes workflow summary from description.
Tested 5/5: all confirm understanding, none create rigid 6-task checklist.
Minimal additions: one paragraph in Overview about scaling effort to task,
a graphviz process flow diagram with the review-loop gate as an explicit
decision point, and GATE wording requiring permission before eliding the
review loop. Tested 5/5 workers now explicitly ask before skipping review.
Two small additions based on tested findings:
- Flowchart Usage: document that decision diamonds in process flows act as
  behavioral enforcement mechanisms (citing 2/5 → 5/5 compliance evidence)
- Bulletproofing: add GATE marker technique for non-optional decision points

Tested 3/3: workers correctly retrieve and apply both techniques.
- brainstorming: check in with user before transitioning to writing-plans
- writing-plans: structured execution handoff (record context, advise
  compaction, give exact continuation prompt with subagent detection)
- SDD: add scaling paragraph with GATE + orchestrator boundary, graphviz
  decision diamonds for review elision, check-in before finishing,
  replace TodoWrite with generic task list language, tighten Red Flags
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude-code Claude Code (Anthropic CLI) issues enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants