Skip to content

可视化头脑风暴重构实施计划

由 Markdown 原样翻译并转换为 Astro Starlight MDX 格式。

对于 agentic workers: REQUIRED: 使用 superpowers:subagent-driven-development (if subagents 可用) or superpowers:executing-plans to implement this plan. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Refactor visual brainstorming from blocking TUI feedback model to non-blocking “Browser Displays, Terminal Commands” architecture.

架构: Browser becomes an interactive display; terminal stays the conversation channel. Server writes user events to a per-screen .events file that Claude 读取 on its next turn. Eliminates wait-for-feedback.sh and all TaskOutput blocking.

Tech Stack: Node.js (Express, ws, chokidar), vanilla HTML/CSS/JS

Spec: docs/superpowers/specs/2026-02-19-visual-brainstorming-refactor-design.md


FileActionResponsibility
lib/brainstorm-server/index.js修改Server: add .events file writing, clear on 新 screen, replace wrapInFrame
lib/brainstorm-server/frame-template.html修改Template: remove feedback footer, add content placeholder + selection indicator
lib/brainstorm-server/helper.js修改Client JS: remove send/feedback functions, narrow to click capture + indicator updates
lib/brainstorm-server/wait-for-feedback.sh删除No longer needed
skills/brainstorming/visual-companion.md修改Skill instructions: rewrite loop to non-blocking flow
tests/brainstorm-server/server.test.js修改Tests: update for 新 模板 structure and helper.js API

Chunk 1: Server, Template, Client, Tests, Skill

Section titled “Chunk 1: Server, Template, Client, Tests, Skill”

文件:

  • 修改: lib/brainstorm-server/frame-template.html

  • 步骤 1: 移除 the feedback footer HTML

替换 the feedback-footer div (lines 227-233) with a selection indicator bar:

<div class="indicator-bar">
<span id="indicator-text">Click an option above, then return to the terminal</span>
</div>

Also replace the default content inside #claude-content (lines 220-223) with the content placeholder:

<div id="claude-content">
<!-- CONTENT -->
</div>
  • 步骤 2: 替换 feedback footer CSS with indicator bar CSS

移除 the .feedback-footer, .feedback-footer label, .feedback-row, and the textarea/button styles within .feedback-footer (lines 82-112).

添加 indicator bar CSS:

.indicator-bar {
background: var(--bg-secondary);
border-top: 1px solid var(--border);
padding: 0.5rem 1.5rem;
flex-shrink: 0;
text-align: center;
}
.indicator-bar span {
font-size: 0.75rem;
color: var(--text-secondary);
}
.indicator-bar .selected-text {
color: var(--accent);
font-weight: 500;
}
  • 步骤 3: 验证 模板 renders

运行 the test suite to check the 模板 still 加载:

Terminal window
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js

预期: Tests 1-5 should still pass. Tests 6-8 may fail (expected — they assert 旧 structure).

  • 步骤 4: 提交
Terminal window
git add lib/brainstorm-server/frame-template.html
git commit -m "Replace feedback footer with selection indicator bar in brainstorm template"

Task 2: 更新 index.js — content injection and .events file

Section titled “Task 2: 更新 index.js — content injection and .events file”

文件:

  • 修改: lib/brainstorm-server/index.js

  • 步骤 1: Write failing test for .events file writing

添加 to tests/brainstorm-server/server.test.js after Test 4 area — a 新 test that sends a WebSocket event with a choice field and verifies .events file is written:

// Test: Choice events written to .events file
console.log('Test: Choice events written to .events file');
const ws3 = new WebSocket(`ws://localhost:${TEST_PORT}`);
await new Promise(resolve => ws3.on('open', resolve));
ws3.send(JSON.stringify({ type: 'click', choice: 'a', text: 'Option A' }));
await sleep(300);
const eventsFile = path.join(TEST_DIR, '.events');
assert(fs.existsSync(eventsFile), '.events file should exist after choice click');
const lines = fs.readFileSync(eventsFile, 'utf-8').trim().split('\n');
const event = JSON.parse(lines[lines.length - 1]);
assert.strictEqual(event.choice, 'a', 'Event should contain choice');
assert.strictEqual(event.text, 'Option A', 'Event should contain text');
ws3.close();
console.log(' PASS');
  • 步骤 2: 运行 test to verify it fails
Terminal window
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js

预期: New test FAILS — .events file doesn’t exist yet.

  • 步骤 3: Write failing test for .events file clearing on 新 screen

添加 another test:

// Test: .events cleared on new screen
console.log('Test: .events cleared on new screen');
// .events file should still exist from previous test
assert(fs.existsSync(path.join(TEST_DIR, '.events')), '.events should exist before new screen');
fs.writeFileSync(path.join(TEST_DIR, 'new-screen.html'), '<h2>New screen</h2>');
await sleep(500);
assert(!fs.existsSync(path.join(TEST_DIR, '.events')), '.events should be cleared after new screen');
console.log(' PASS');
  • 步骤 4: 运行 test to verify it fails
Terminal window
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js

预期: New test FAILS — .events not cleared on screen push.

  • 步骤 5: Implement .events file writing in index.js

In the WebSocket message handler (line 74-77 of index.js), after the console.log, add:

// Write user events to .events file for Claude to read
if (event.choice) {
const eventsFile = path.join(SCREEN_DIR, '.events');
fs.appendFileSync(eventsFile, JSON.stringify(event) + '\n');
}

In the chokidar add handler (line 104-111), add .events clearing:

if (filePath.endsWith('.html')) {
// Clear events from previous screen
const eventsFile = path.join(SCREEN_DIR, '.events');
if (fs.existsSync(eventsFile)) fs.unlinkSync(eventsFile);
console.log(JSON.stringify({ type: 'screen-added', file: filePath }));
// ... existing reload broadcast
}
  • 步骤 6: 替换 wrapInFrame with comment placeholder injection

替换 the wrapInFrame function (lines 27-32 of index.js):

function wrapInFrame(content) {
return frameTemplate.replace('<!-- CONTENT -->', content);
}
  • 步骤 7: 运行 all tests
Terminal window
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js

预期: New .events tests PASS. Existing tests may still have 失败 from 旧 assertions (fixed in Task 4).

  • 步骤 8: 提交
Terminal window
git add lib/brainstorm-server/index.js tests/brainstorm-server/server.test.js
git commit -m "Add .events file writing and comment-based content injection to brainstorm server"

文件:

  • 修改: lib/brainstorm-server/helper.js

  • 步骤 1: 移除 sendToClaude function

删除 the sendToClaude function (lines 92-106) — the function body and the page takeover HTML.

  • 步骤 2: 移除 window.send function

删除 the window.send function (lines 120-129) — was tied to the removed Send button.

  • 步骤 3: 移除 form submission and input change handlers

删除 the form submission handler (lines 57-71) and the input change handler (lines 73-89) including the inputTimeout variable.

  • 步骤 4: 移除 pageshow event listener

删除 the pageshow listener we added earlier (no textarea to clear anymore).

  • 步骤 5: Narrow click handler to [data-choice] only

替换 the click handler (lines 36-55) with a narrower version:

// Capture clicks on choice elements
document.addEventListener('click', (e) => {
const target = e.target.closest('[data-choice]');
if (!target) return;
sendEvent({
type: 'click',
text: target.textContent.trim(),
choice: target.dataset.choice,
id: target.id || null
});
});
  • 步骤 6: 添加 indicator bar update on choice click

After the sendEvent call in the click handler, add:

// Update indicator bar
const indicator = document.getElementById('indicator-text');
if (indicator) {
const label = target.querySelector('h3, .content h3, .card-body h3')?.textContent?.trim() || target.dataset.choice;
indicator.innerHTML = '<span class="selected-text">' + label + ' selected</span> — return to terminal to continue';
}
  • 步骤 7: 移除 sendToClaude from window.brainstorm API

更新 the window.brainstorm object (lines 132-136) to remove sendToClaude:

window.brainstorm = {
send: sendEvent,
choice: (value, metadata = {}) => sendEvent({ type: 'choice', value, ...metadata })
};
  • 步骤 8: 运行 tests
Terminal window
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js
  • 步骤 9: 提交
Terminal window
git add lib/brainstorm-server/helper.js
git commit -m "Simplify helper.js: remove feedback functions, narrow to choice capture + indicator"

文件:

  • 修改: tests/brainstorm-server/server.test.js

Note: Line 引用 below are from the original file. Task 2 inserted 新 tests earlier in the file, so actual line numbers will be shifted. Find tests by their console.log labels (e.g., “Test 5:”, “Test 6:”).

  • 步骤 1: 更新 Test 5 (full document assertion)

Find the Test 5 assertion !fullRes.body.includes('feedback-footer'). Change it to: Full documents should NOT have the indicator bar either (they’re served as-is):

assert(!fullRes.body.includes('indicator-bar') || fullDoc.includes('indicator-bar'),
'Should not wrap full documents in frame template');
  • 步骤 2: 更新 Test 6 (fragment wrapping)

Line 125: 替换 feedback-footer assertion with indicator bar assertion:

assert(fragRes.body.includes('indicator-bar'), 'Fragment should get indicator bar from frame');

Also verify content placeholder was replaced (fragment content appears, placeholder comment doesn’t):

assert(!fragRes.body.includes('<!-- CONTENT -->'), 'Content placeholder should be replaced');
  • 步骤 3: 更新 Test 7 (helper.js API)

Lines 140-142: 更新 assertions to reflect the 新 API surface:

assert(helperContent.includes('toggleSelect'), 'helper.js should define toggleSelect');
assert(helperContent.includes('sendEvent'), 'helper.js should define sendEvent');
assert(helperContent.includes('selectedChoice'), 'helper.js should track selectedChoice');
assert(helperContent.includes('brainstorm'), 'helper.js should expose brainstorm API');
assert(!helperContent.includes('sendToClaude'), 'helper.js should not contain sendToClaude');
  • 步骤 4: 替换 Test 8 (sendToClaude theming) with indicator bar test

替换 Test 8 (lines 145-149) — sendToClaude no longer exists. Test the indicator bar instead:

// Test 8: Indicator bar uses CSS variables (theme support)
console.log('Test 8: Indicator bar uses CSS variables');
const templateContent = fs.readFileSync(
path.join(__dirname, '../../lib/brainstorm-server/frame-template.html'), 'utf-8'
);
assert(templateContent.includes('indicator-bar'), 'Template should have indicator bar');
assert(templateContent.includes('indicator-text'), 'Template should have indicator text element');
console.log(' PASS');
  • 步骤 5: 运行 full test suite
Terminal window
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js

预期: ALL tests PASS.

  • 步骤 6: 提交
Terminal window
git add tests/brainstorm-server/server.test.js
git commit -m "Update brainstorm server tests for new template structure and helper.js API"

文件:

  • 删除: lib/brainstorm-server/wait-for-feedback.sh

  • 步骤 1: 验证 no other files import or 引用 wait-for-feedback.sh

Search the codebase:

Terminal window
grep -r "wait-for-feedback" /Users/drewritter/prime-rad/superpowers/ --include="*.js" --include="*.md" --include="*.sh" --include="*.json"

预期 引用: only visual-companion.md (rewritten in Task 6) and possibly release notes (historical, leave as-is).

  • 步骤 2: 删除 the file
Terminal window
rm lib/brainstorm-server/wait-for-feedback.sh
  • 步骤 3: 运行 tests to confirm nothing breaks
Terminal window
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js

预期: All tests PASS (no test referenced this file).

  • 步骤 4: 提交
Terminal window
git add -u lib/brainstorm-server/wait-for-feedback.sh
git commit -m "Delete wait-for-feedback.sh: replaced by .events file"

文件:

  • 修改: skills/brainstorming/visual-companion.md

  • 步骤 1: 更新 “How It Works” description (line 18)

替换 the sentence about receiving feedback “as JSON” with:

The server watches a directory for HTML files and serves the newest one to the browser. You write HTML content, the user sees it in their browser and can click to select options. Selections are recorded to a `.events` file that you read on your next turn.
  • 步骤 2: 更新 fragment description (line 20)

移除 “feedback footer” from the description of what the frame 模板 提供:

**Content fragments vs full documents:** If your HTML file starts with `<!DOCTYPE` or `<html`, the server serves it as-is (just injects the helper script). Otherwise, the server automatically wraps your content in the frame template — adding the header, CSS theme, selection indicator, and all interactive infrastructure. **Write content fragments by default.** Only write full documents when you need complete control over the page.
  • 步骤 3: Rewrite “The Loop” section (lines 36-61)

替换 the entire “The Loop” section with:

## The Loop
1. **Write HTML** to a new file in `screen_dir`:
- Use semantic filenames: `platform.html`, `visual-style.html`, `layout.html`
- **Never reuse filenames** — each screen gets a fresh file
- Use Write tool — **never use cat/heredoc** (dumps noise into terminal)
- Server automatically serves the newest file
2. **Tell user what to expect and end your turn:**
- Remind them of the URL (every step, not just first)
- Give a brief text summary of what's on screen (e.g., "Showing 3 layout options for the homepage")
- Ask them to respond in the terminal: "Take a look and let me know what you think. Click to select an option if you'd like."
3. **On your next turn** — after the user responds in the terminal:
- Read `$SCREEN_DIR/.events` if it exists — this contains the user's browser interactions (clicks, selections) as JSON lines
- Merge with the user's terminal text to get the full picture
- The terminal message is the primary feedback; `.events` provides structured interaction data
4. **Iterate or advance** — if feedback changes current screen, write a new file (e.g., `layout-v2.html`). Only move to the next question when the current step is validated.
5. Repeat until done.
  • 步骤 4: 替换 “User Feedback Format” section (lines 165-174)

替换为:

## Browser Events Format
When the user clicks options in the browser, their interactions are recorded to `$SCREEN_DIR/.events` (one JSON object per line). The file is cleared automatically when you push a new screen.
```jsonl
{"type":"click","choice":"a","text":"Option A - Simple Layout","timestamp":1706000101}
{"type":"click","choice":"c","text":"Option C - Complex Grid","timestamp":1706000108}
{"type":"click","choice":"b","text":"Option B - Hybrid","timestamp":1706000115}

The full event stream shows the user’s exploration path — they may click multiple options before settling. The last choice event is typically the final selection, but the pattern of clicks can reveal hesitation or preferences worth asking about.

If .events doesn’t exist, the user didn’t interact with the browser — use only their terminal text.

- [ ] **步骤 5: 更新 "Writing Content Fragments" description (line 65)**
移除 "feedback footer" 引用:
```markdown
Write just the content that goes inside the page. The server wraps it in the frame template automatically (header, theme CSS, selection indicator, and all interactive infrastructure).
  • 步骤 6: 更新 Reference section (lines 200-203)

移除 the helper.js 引用 description about “JS API” — the API is now minimal. Keep the path 引用:

## Reference
- Frame template (CSS reference): `${CLAUDE_PLUGIN_ROOT}/lib/brainstorm-server/frame-template.html`
- Helper script (client-side): `${CLAUDE_PLUGIN_ROOT}/lib/brainstorm-server/helper.js`
  • 步骤 7: 提交
Terminal window
git add skills/brainstorming/visual-companion.md
git commit -m "Rewrite visual-companion.md for non-blocking browser-displays-terminal-commands flow"

  • 步骤 1: 运行 full test suite
Terminal window
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js

预期: ALL tests PASS.

  • 步骤 2: Manual smoke test

Start the 服务器 manually and verify the flow works end-to-end:

Terminal window
cd /Users/drewritter/prime-rad/superpowers && lib/brainstorm-server/start-server.sh --project-dir /tmp/brainstorm-smoke-test

Write a test fragment, open in browser, click an option, verify .events file is written, verify indicator bar updates. 然后 停止 the 服务器:

Terminal window
lib/brainstorm-server/stop-server.sh <screen_dir from start output>
  • 步骤 3: 验证 no stale 引用 remain
Terminal window
grep -r "wait-for-feedback\|sendToClaude\|feedback-footer\|send-to-claude\|TaskOutput.*block.*true" /Users/drewritter/prime-rad/superpowers/ --include="*.js" --include="*.md" --include="*.sh" --include="*.html" | grep -v node_modules | grep -v RELEASE-NOTES | grep -v "\.md:.*spec\|plan"

预期: No hits outside of release notes and the spec/plan docs (which are historical).

  • 步骤 4: Final commit if any 清理 needed
Terminal window
git status
# Review untracked/modified files, stage specific files as needed, commit if clean
-
0:000:00