可视化头脑风暴重构实施计划
Section titled “可视化头脑风暴重构实施计划”对于 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
File Map
Section titled “File Map”| File | Action | Responsibility |
|---|---|---|
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”Task 1: 更新 frame-template.html
Section titled “Task 1: 更新 frame-template.html”文件:
-
修改:
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 加载:
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: 提交
git add lib/brainstorm-server/frame-template.htmlgit 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
.eventsfile 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
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
.eventsfile 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
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js预期: New test FAILS — .events not cleared on screen push.
- 步骤 5: Implement
.eventsfile writing inindex.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: 替换
wrapInFramewith comment placeholder injection
替换 the wrapInFrame function (lines 27-32 of index.js):
function wrapInFrame(content) { return frameTemplate.replace('<!-- CONTENT -->', content);}- 步骤 7: 运行 all tests
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: 提交
git add lib/brainstorm-server/index.js tests/brainstorm-server/server.test.jsgit commit -m "Add .events file writing and comment-based content injection to brainstorm server"Task 3: Simplify helper.js
Section titled “Task 3: Simplify helper.js”文件:
-
修改:
lib/brainstorm-server/helper.js -
步骤 1: 移除
sendToClaudefunction
删除 the sendToClaude function (lines 92-106) — the function body and the page takeover HTML.
- 步骤 2: 移除
window.sendfunction
删除 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: 移除
pageshowevent 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: 移除
sendToClaudefromwindow.brainstormAPI
更新 the window.brainstorm object (lines 132-136) to remove sendToClaude:
window.brainstorm = { send: sendEvent, choice: (value, metadata = {}) => sendEvent({ type: 'choice', value, ...metadata }) };- 步骤 8: 运行 tests
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js- 步骤 9: 提交
git add lib/brainstorm-server/helper.jsgit commit -m "Simplify helper.js: remove feedback functions, narrow to choice capture + indicator"Task 4: 更新 tests for 新 structure
Section titled “Task 4: 更新 tests for 新 structure”文件:
- 修改:
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
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js预期: ALL tests PASS.
- 步骤 6: 提交
git add tests/brainstorm-server/server.test.jsgit commit -m "Update brainstorm server tests for new template structure and helper.js API"Task 5: 删除 wait-for-feedback.sh
Section titled “Task 5: 删除 wait-for-feedback.sh”文件:
-
删除:
lib/brainstorm-server/wait-for-feedback.sh -
步骤 1: 验证 no other files import or 引用
wait-for-feedback.sh
Search the codebase:
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
rm lib/brainstorm-server/wait-for-feedback.sh- 步骤 3: 运行 tests to confirm nothing breaks
cd /Users/drewritter/prime-rad/superpowers && node tests/brainstorm-server/server.test.js预期: All tests PASS (no test referenced this file).
- 步骤 4: 提交
git add -u lib/brainstorm-server/wait-for-feedback.shgit commit -m "Delete wait-for-feedback.sh: replaced by .events file"Task 6: Rewrite visual-companion.md
Section titled “Task 6: Rewrite visual-companion.md”文件:
-
修改:
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" 引用:
```markdownWrite 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: 提交
git add skills/brainstorming/visual-companion.mdgit commit -m "Rewrite visual-companion.md for non-blocking browser-displays-terminal-commands flow"Task 7: Final 验证
Section titled “Task 7: Final 验证”- 步骤 1: 运行 full test suite
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:
cd /Users/drewritter/prime-rad/superpowers && lib/brainstorm-server/start-server.sh --project-dir /tmp/brainstorm-smoke-testWrite a test fragment, open in browser, click an option, verify .events file is written, verify indicator bar updates. 然后 停止 the 服务器:
lib/brainstorm-server/stop-server.sh <screen_dir from start output>- 步骤 3: 验证 no stale 引用 remain
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
git status# Review untracked/modified files, stage specific files as needed, commit if clean