workerssuper 5.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/.claude-plugin/marketplace.json +20 -0
  2. package/.claude-plugin/plugin.json +13 -0
  3. package/.codex/INSTALL.md +67 -0
  4. package/.cursor-plugin/plugin.json +18 -0
  5. package/.gitattributes +18 -0
  6. package/.github/FUNDING.yml +3 -0
  7. package/.github/ISSUE_TEMPLATE/bug_report.md +52 -0
  8. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  9. package/.github/ISSUE_TEMPLATE/feature_request.md +34 -0
  10. package/.github/ISSUE_TEMPLATE/platform_support.md +23 -0
  11. package/.github/PULL_REQUEST_TEMPLATE.md +87 -0
  12. package/.opencode/INSTALL.md +83 -0
  13. package/.opencode/plugins/superpowers.js +107 -0
  14. package/CHANGELOG.md +13 -0
  15. package/CODE_OF_CONDUCT.md +128 -0
  16. package/GEMINI.md +2 -0
  17. package/LICENSE +21 -0
  18. package/README.md +187 -0
  19. package/RELEASE-NOTES.md +1057 -0
  20. package/agents/code-reviewer.md +48 -0
  21. package/commands/brainstorm.md +5 -0
  22. package/commands/execute-plan.md +5 -0
  23. package/commands/write-plan.md +5 -0
  24. package/docs/README.codex.md +126 -0
  25. package/docs/README.opencode.md +130 -0
  26. package/docs/plans/2025-11-22-opencode-support-design.md +294 -0
  27. package/docs/plans/2025-11-22-opencode-support-implementation.md +1095 -0
  28. package/docs/plans/2025-11-28-skills-improvements-from-user-feedback.md +711 -0
  29. package/docs/plans/2026-01-17-visual-brainstorming.md +571 -0
  30. package/docs/superpowers/plans/2026-01-22-document-review-system.md +301 -0
  31. package/docs/superpowers/plans/2026-02-19-visual-brainstorming-refactor.md +523 -0
  32. package/docs/superpowers/plans/2026-03-11-zero-dep-brainstorm-server.md +479 -0
  33. package/docs/superpowers/specs/2026-01-22-document-review-system-design.md +136 -0
  34. package/docs/superpowers/specs/2026-02-19-visual-brainstorming-refactor-design.md +162 -0
  35. package/docs/superpowers/specs/2026-03-11-zero-dep-brainstorm-server-design.md +118 -0
  36. package/docs/testing.md +303 -0
  37. package/docs/windows/polyglot-hooks.md +212 -0
  38. package/gemini-extension.json +6 -0
  39. package/hooks/hooks-cursor.json +10 -0
  40. package/hooks/hooks.json +16 -0
  41. package/hooks/run-hook.cmd +46 -0
  42. package/hooks/session-start +57 -0
  43. package/package.json +5 -0
  44. package/skills/brainstorming/SKILL.md +164 -0
  45. package/skills/brainstorming/scripts/frame-template.html +214 -0
  46. package/skills/brainstorming/scripts/helper.js +88 -0
  47. package/skills/brainstorming/scripts/server.cjs +338 -0
  48. package/skills/brainstorming/scripts/start-server.sh +153 -0
  49. package/skills/brainstorming/scripts/stop-server.sh +55 -0
  50. package/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  51. package/skills/brainstorming/visual-companion.md +286 -0
  52. package/skills/dispatching-parallel-agents/SKILL.md +182 -0
  53. package/skills/executing-plans/SKILL.md +70 -0
  54. package/skills/finishing-a-development-branch/SKILL.md +200 -0
  55. package/skills/receiving-code-review/SKILL.md +213 -0
  56. package/skills/requesting-code-review/SKILL.md +105 -0
  57. package/skills/requesting-code-review/code-reviewer.md +146 -0
  58. package/skills/subagent-driven-development/SKILL.md +277 -0
  59. package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
  60. package/skills/subagent-driven-development/implementer-prompt.md +113 -0
  61. package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
  62. package/skills/systematic-debugging/CREATION-LOG.md +119 -0
  63. package/skills/systematic-debugging/SKILL.md +296 -0
  64. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  65. package/skills/systematic-debugging/condition-based-waiting.md +115 -0
  66. package/skills/systematic-debugging/defense-in-depth.md +122 -0
  67. package/skills/systematic-debugging/find-polluter.sh +63 -0
  68. package/skills/systematic-debugging/root-cause-tracing.md +169 -0
  69. package/skills/systematic-debugging/test-academic.md +14 -0
  70. package/skills/systematic-debugging/test-pressure-1.md +58 -0
  71. package/skills/systematic-debugging/test-pressure-2.md +68 -0
  72. package/skills/systematic-debugging/test-pressure-3.md +69 -0
  73. package/skills/test-driven-development/SKILL.md +371 -0
  74. package/skills/test-driven-development/testing-anti-patterns.md +299 -0
  75. package/skills/using-git-worktrees/SKILL.md +218 -0
  76. package/skills/using-superpowers/SKILL.md +115 -0
  77. package/skills/using-superpowers/references/codex-tools.md +25 -0
  78. package/skills/using-superpowers/references/gemini-tools.md +33 -0
  79. package/skills/verification-before-completion/SKILL.md +139 -0
  80. package/skills/writing-plans/SKILL.md +145 -0
  81. package/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
  82. package/skills/writing-skills/SKILL.md +655 -0
  83. package/skills/writing-skills/anthropic-best-practices.md +1150 -0
  84. package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  85. package/skills/writing-skills/graphviz-conventions.dot +172 -0
  86. package/skills/writing-skills/persuasion-principles.md +187 -0
  87. package/skills/writing-skills/render-graphs.js +168 -0
  88. package/skills/writing-skills/testing-skills-with-subagents.md +384 -0
  89. package/tests/brainstorm-server/package-lock.json +36 -0
  90. package/tests/brainstorm-server/package.json +10 -0
  91. package/tests/brainstorm-server/server.test.js +424 -0
  92. package/tests/brainstorm-server/windows-lifecycle.test.sh +351 -0
  93. package/tests/brainstorm-server/ws-protocol.test.js +392 -0
  94. package/tests/claude-code/README.md +158 -0
  95. package/tests/claude-code/analyze-token-usage.py +168 -0
  96. package/tests/claude-code/run-skill-tests.sh +187 -0
  97. package/tests/claude-code/test-document-review-system.sh +177 -0
  98. package/tests/claude-code/test-helpers.sh +202 -0
  99. package/tests/claude-code/test-subagent-driven-development-integration.sh +314 -0
  100. package/tests/claude-code/test-subagent-driven-development.sh +165 -0
  101. package/tests/explicit-skill-requests/prompts/action-oriented.txt +3 -0
  102. package/tests/explicit-skill-requests/prompts/after-planning-flow.txt +17 -0
  103. package/tests/explicit-skill-requests/prompts/claude-suggested-it.txt +11 -0
  104. package/tests/explicit-skill-requests/prompts/i-know-what-sdd-means.txt +8 -0
  105. package/tests/explicit-skill-requests/prompts/mid-conversation-execute-plan.txt +3 -0
  106. package/tests/explicit-skill-requests/prompts/please-use-brainstorming.txt +1 -0
  107. package/tests/explicit-skill-requests/prompts/skip-formalities.txt +3 -0
  108. package/tests/explicit-skill-requests/prompts/subagent-driven-development-please.txt +1 -0
  109. package/tests/explicit-skill-requests/prompts/use-systematic-debugging.txt +1 -0
  110. package/tests/explicit-skill-requests/run-all.sh +70 -0
  111. package/tests/explicit-skill-requests/run-claude-describes-sdd.sh +100 -0
  112. package/tests/explicit-skill-requests/run-extended-multiturn-test.sh +113 -0
  113. package/tests/explicit-skill-requests/run-haiku-test.sh +144 -0
  114. package/tests/explicit-skill-requests/run-multiturn-test.sh +143 -0
  115. package/tests/explicit-skill-requests/run-test.sh +136 -0
  116. package/tests/opencode/run-tests.sh +163 -0
  117. package/tests/opencode/setup.sh +73 -0
  118. package/tests/opencode/test-plugin-loading.sh +72 -0
  119. package/tests/opencode/test-priority.sh +198 -0
  120. package/tests/opencode/test-tools.sh +104 -0
  121. package/tests/skill-triggering/prompts/dispatching-parallel-agents.txt +8 -0
  122. package/tests/skill-triggering/prompts/executing-plans.txt +1 -0
  123. package/tests/skill-triggering/prompts/requesting-code-review.txt +3 -0
  124. package/tests/skill-triggering/prompts/systematic-debugging.txt +11 -0
  125. package/tests/skill-triggering/prompts/test-driven-development.txt +7 -0
  126. package/tests/skill-triggering/prompts/writing-plans.txt +10 -0
  127. package/tests/skill-triggering/run-all.sh +60 -0
  128. package/tests/skill-triggering/run-test.sh +88 -0
  129. package/tests/subagent-driven-dev/go-fractals/design.md +81 -0
  130. package/tests/subagent-driven-dev/go-fractals/plan.md +172 -0
  131. package/tests/subagent-driven-dev/go-fractals/scaffold.sh +45 -0
  132. package/tests/subagent-driven-dev/run-test.sh +106 -0
  133. package/tests/subagent-driven-dev/svelte-todo/design.md +70 -0
  134. package/tests/subagent-driven-dev/svelte-todo/plan.md +222 -0
  135. package/tests/subagent-driven-dev/svelte-todo/scaffold.sh +46 -0
@@ -0,0 +1,392 @@
1
+ /**
2
+ * Unit tests for the zero-dependency WebSocket protocol implementation.
3
+ *
4
+ * Tests the WebSocket frame encoding/decoding, handshake computation,
5
+ * and protocol-level behavior independent of the HTTP server.
6
+ *
7
+ * The module under test exports:
8
+ * - computeAcceptKey(clientKey) -> string
9
+ * - encodeFrame(opcode, payload) -> Buffer
10
+ * - decodeFrame(buffer) -> { opcode, payload, bytesConsumed } | null
11
+ * - OPCODES: { TEXT, CLOSE, PING, PONG }
12
+ */
13
+
14
+ const assert = require('assert');
15
+ const crypto = require('crypto');
16
+ const path = require('path');
17
+
18
+ // The module under test — will be the new zero-dep server file
19
+ const SERVER_PATH = path.join(__dirname, '../../skills/brainstorming/scripts/server.cjs');
20
+ let ws;
21
+
22
+ try {
23
+ ws = require(SERVER_PATH);
24
+ } catch (e) {
25
+ // Module doesn't exist yet (TDD — tests written before implementation)
26
+ console.error(`Cannot load ${SERVER_PATH}: ${e.message}`);
27
+ console.error('This is expected if running tests before implementation.');
28
+ process.exit(1);
29
+ }
30
+
31
+ function runTests() {
32
+ let passed = 0;
33
+ let failed = 0;
34
+
35
+ function test(name, fn) {
36
+ try {
37
+ fn();
38
+ console.log(` PASS: ${name}`);
39
+ passed++;
40
+ } catch (e) {
41
+ console.log(` FAIL: ${name}`);
42
+ console.log(` ${e.message}`);
43
+ failed++;
44
+ }
45
+ }
46
+
47
+ // ========== Handshake ==========
48
+ console.log('\n--- WebSocket Handshake ---');
49
+
50
+ test('computeAcceptKey produces correct RFC 6455 accept value', () => {
51
+ // RFC 6455 Section 4.2.2 example
52
+ // The magic GUID is "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
53
+ const clientKey = 'dGhlIHNhbXBsZSBub25jZQ==';
54
+ const expected = 's3pPLMBiTxaQ9kYGzzhZRbK+xOo=';
55
+ assert.strictEqual(ws.computeAcceptKey(clientKey), expected);
56
+ });
57
+
58
+ test('computeAcceptKey produces valid base64 for random keys', () => {
59
+ for (let i = 0; i < 10; i++) {
60
+ const randomKey = crypto.randomBytes(16).toString('base64');
61
+ const result = ws.computeAcceptKey(randomKey);
62
+ // Result should be valid base64
63
+ assert.strictEqual(Buffer.from(result, 'base64').toString('base64'), result);
64
+ // SHA-1 output is 20 bytes, base64 encoded = 28 chars
65
+ assert.strictEqual(result.length, 28);
66
+ }
67
+ });
68
+
69
+ // ========== Frame Encoding ==========
70
+ console.log('\n--- Frame Encoding (server -> client) ---');
71
+
72
+ test('encodes small text frame (< 126 bytes)', () => {
73
+ const payload = 'Hello';
74
+ const frame = ws.encodeFrame(ws.OPCODES.TEXT, Buffer.from(payload));
75
+ // FIN bit + TEXT opcode = 0x81, length = 5
76
+ assert.strictEqual(frame[0], 0x81);
77
+ assert.strictEqual(frame[1], 5);
78
+ assert.strictEqual(frame.slice(2).toString(), 'Hello');
79
+ assert.strictEqual(frame.length, 7);
80
+ });
81
+
82
+ test('encodes empty text frame', () => {
83
+ const frame = ws.encodeFrame(ws.OPCODES.TEXT, Buffer.alloc(0));
84
+ assert.strictEqual(frame[0], 0x81);
85
+ assert.strictEqual(frame[1], 0);
86
+ assert.strictEqual(frame.length, 2);
87
+ });
88
+
89
+ test('encodes medium text frame (126-65535 bytes)', () => {
90
+ const payload = Buffer.alloc(200, 0x41); // 200 'A's
91
+ const frame = ws.encodeFrame(ws.OPCODES.TEXT, payload);
92
+ assert.strictEqual(frame[0], 0x81);
93
+ assert.strictEqual(frame[1], 126); // extended length marker
94
+ assert.strictEqual(frame.readUInt16BE(2), 200);
95
+ assert.strictEqual(frame.slice(4).toString(), payload.toString());
96
+ assert.strictEqual(frame.length, 204);
97
+ });
98
+
99
+ test('encodes frame at exactly 126 bytes (boundary)', () => {
100
+ const payload = Buffer.alloc(126, 0x42);
101
+ const frame = ws.encodeFrame(ws.OPCODES.TEXT, payload);
102
+ assert.strictEqual(frame[1], 126); // extended length marker
103
+ assert.strictEqual(frame.readUInt16BE(2), 126);
104
+ assert.strictEqual(frame.length, 130);
105
+ });
106
+
107
+ test('encodes frame at exactly 125 bytes (max small)', () => {
108
+ const payload = Buffer.alloc(125, 0x43);
109
+ const frame = ws.encodeFrame(ws.OPCODES.TEXT, payload);
110
+ assert.strictEqual(frame[1], 125);
111
+ assert.strictEqual(frame.length, 127);
112
+ });
113
+
114
+ test('encodes large frame (> 65535 bytes)', () => {
115
+ const payload = Buffer.alloc(70000, 0x44);
116
+ const frame = ws.encodeFrame(ws.OPCODES.TEXT, payload);
117
+ assert.strictEqual(frame[0], 0x81);
118
+ assert.strictEqual(frame[1], 127); // 64-bit length marker
119
+ // 8-byte extended length at offset 2
120
+ const len = Number(frame.readBigUInt64BE(2));
121
+ assert.strictEqual(len, 70000);
122
+ assert.strictEqual(frame.length, 10 + 70000);
123
+ });
124
+
125
+ test('encodes close frame', () => {
126
+ const frame = ws.encodeFrame(ws.OPCODES.CLOSE, Buffer.alloc(0));
127
+ assert.strictEqual(frame[0], 0x88); // FIN + CLOSE
128
+ assert.strictEqual(frame[1], 0);
129
+ });
130
+
131
+ test('encodes pong frame with payload', () => {
132
+ const payload = Buffer.from('ping-data');
133
+ const frame = ws.encodeFrame(ws.OPCODES.PONG, payload);
134
+ assert.strictEqual(frame[0], 0x8A); // FIN + PONG
135
+ assert.strictEqual(frame[1], payload.length);
136
+ assert.strictEqual(frame.slice(2).toString(), 'ping-data');
137
+ });
138
+
139
+ test('server frames are never masked (per RFC 6455)', () => {
140
+ const frame = ws.encodeFrame(ws.OPCODES.TEXT, Buffer.from('test'));
141
+ // Bit 7 of byte 1 is the mask bit — must be 0 for server frames
142
+ assert.strictEqual(frame[1] & 0x80, 0);
143
+ });
144
+
145
+ // ========== Frame Decoding ==========
146
+ console.log('\n--- Frame Decoding (client -> server) ---');
147
+
148
+ // Helper: create a masked client frame
149
+ function makeClientFrame(opcode, payload, fin = true) {
150
+ const buf = Buffer.from(payload);
151
+ const mask = crypto.randomBytes(4);
152
+ const masked = Buffer.alloc(buf.length);
153
+ for (let i = 0; i < buf.length; i++) {
154
+ masked[i] = buf[i] ^ mask[i % 4];
155
+ }
156
+
157
+ let header;
158
+ const finBit = fin ? 0x80 : 0x00;
159
+ if (buf.length < 126) {
160
+ header = Buffer.alloc(6);
161
+ header[0] = finBit | opcode;
162
+ header[1] = 0x80 | buf.length; // mask bit set
163
+ mask.copy(header, 2);
164
+ } else if (buf.length < 65536) {
165
+ header = Buffer.alloc(8);
166
+ header[0] = finBit | opcode;
167
+ header[1] = 0x80 | 126;
168
+ header.writeUInt16BE(buf.length, 2);
169
+ mask.copy(header, 4);
170
+ } else {
171
+ header = Buffer.alloc(14);
172
+ header[0] = finBit | opcode;
173
+ header[1] = 0x80 | 127;
174
+ header.writeBigUInt64BE(BigInt(buf.length), 2);
175
+ mask.copy(header, 10);
176
+ }
177
+
178
+ return Buffer.concat([header, masked]);
179
+ }
180
+
181
+ test('decodes small masked text frame', () => {
182
+ const frame = makeClientFrame(0x01, 'Hello');
183
+ const result = ws.decodeFrame(frame);
184
+ assert(result, 'Should return a result');
185
+ assert.strictEqual(result.opcode, ws.OPCODES.TEXT);
186
+ assert.strictEqual(result.payload.toString(), 'Hello');
187
+ assert.strictEqual(result.bytesConsumed, frame.length);
188
+ });
189
+
190
+ test('decodes empty masked text frame', () => {
191
+ const frame = makeClientFrame(0x01, '');
192
+ const result = ws.decodeFrame(frame);
193
+ assert(result, 'Should return a result');
194
+ assert.strictEqual(result.opcode, ws.OPCODES.TEXT);
195
+ assert.strictEqual(result.payload.length, 0);
196
+ });
197
+
198
+ test('decodes medium masked text frame (126-65535 bytes)', () => {
199
+ const payload = 'A'.repeat(200);
200
+ const frame = makeClientFrame(0x01, payload);
201
+ const result = ws.decodeFrame(frame);
202
+ assert(result, 'Should return a result');
203
+ assert.strictEqual(result.payload.toString(), payload);
204
+ });
205
+
206
+ test('decodes large masked text frame (> 65535 bytes)', () => {
207
+ const payload = 'B'.repeat(70000);
208
+ const frame = makeClientFrame(0x01, payload);
209
+ const result = ws.decodeFrame(frame);
210
+ assert(result, 'Should return a result');
211
+ assert.strictEqual(result.payload.length, 70000);
212
+ assert.strictEqual(result.payload.toString(), payload);
213
+ });
214
+
215
+ test('decodes masked close frame', () => {
216
+ const frame = makeClientFrame(0x08, '');
217
+ const result = ws.decodeFrame(frame);
218
+ assert(result, 'Should return a result');
219
+ assert.strictEqual(result.opcode, ws.OPCODES.CLOSE);
220
+ });
221
+
222
+ test('decodes masked ping frame', () => {
223
+ const frame = makeClientFrame(0x09, 'ping!');
224
+ const result = ws.decodeFrame(frame);
225
+ assert(result, 'Should return a result');
226
+ assert.strictEqual(result.opcode, ws.OPCODES.PING);
227
+ assert.strictEqual(result.payload.toString(), 'ping!');
228
+ });
229
+
230
+ test('returns null for incomplete frame (not enough header bytes)', () => {
231
+ const result = ws.decodeFrame(Buffer.from([0x81]));
232
+ assert.strictEqual(result, null, 'Should return null for 1-byte buffer');
233
+ });
234
+
235
+ test('returns null for incomplete frame (header ok, payload truncated)', () => {
236
+ // Create a valid frame then truncate it
237
+ const frame = makeClientFrame(0x01, 'Hello World');
238
+ const truncated = frame.slice(0, frame.length - 3);
239
+ const result = ws.decodeFrame(truncated);
240
+ assert.strictEqual(result, null, 'Should return null for truncated frame');
241
+ });
242
+
243
+ test('returns null for incomplete extended-length header', () => {
244
+ // Frame claiming 16-bit length but only 3 bytes total
245
+ const buf = Buffer.alloc(3);
246
+ buf[0] = 0x81;
247
+ buf[1] = 0x80 | 126; // masked, 16-bit extended
248
+ // Missing the 2 length bytes + mask
249
+ const result = ws.decodeFrame(buf);
250
+ assert.strictEqual(result, null);
251
+ });
252
+
253
+ test('rejects unmasked client frame', () => {
254
+ // Server MUST reject unmasked client frames per RFC 6455 Section 5.1
255
+ const buf = Buffer.alloc(7);
256
+ buf[0] = 0x81; // FIN + TEXT
257
+ buf[1] = 5; // length 5, NO mask bit
258
+ Buffer.from('Hello').copy(buf, 2);
259
+ assert.throws(() => ws.decodeFrame(buf), /mask/i, 'Should reject unmasked client frame');
260
+ });
261
+
262
+ test('handles multiple frames in a single buffer', () => {
263
+ const frame1 = makeClientFrame(0x01, 'first');
264
+ const frame2 = makeClientFrame(0x01, 'second');
265
+ const combined = Buffer.concat([frame1, frame2]);
266
+
267
+ const result1 = ws.decodeFrame(combined);
268
+ assert(result1, 'Should decode first frame');
269
+ assert.strictEqual(result1.payload.toString(), 'first');
270
+ assert.strictEqual(result1.bytesConsumed, frame1.length);
271
+
272
+ const result2 = ws.decodeFrame(combined.slice(result1.bytesConsumed));
273
+ assert(result2, 'Should decode second frame');
274
+ assert.strictEqual(result2.payload.toString(), 'second');
275
+ });
276
+
277
+ test('correctly unmasks with all mask byte values', () => {
278
+ // Use a known mask to verify unmasking arithmetic
279
+ const payload = Buffer.from('ABCDEFGH');
280
+ const mask = Buffer.from([0xFF, 0x00, 0xAA, 0x55]);
281
+ const masked = Buffer.alloc(payload.length);
282
+ for (let i = 0; i < payload.length; i++) {
283
+ masked[i] = payload[i] ^ mask[i % 4];
284
+ }
285
+
286
+ // Build frame manually
287
+ const header = Buffer.alloc(6);
288
+ header[0] = 0x81; // FIN + TEXT
289
+ header[1] = 0x80 | payload.length;
290
+ mask.copy(header, 2);
291
+ const frame = Buffer.concat([header, masked]);
292
+
293
+ const result = ws.decodeFrame(frame);
294
+ assert.strictEqual(result.payload.toString(), 'ABCDEFGH');
295
+ });
296
+
297
+ // ========== Frame Encoding Boundary at 65535/65536 ==========
298
+ console.log('\n--- Frame Size Boundaries ---');
299
+
300
+ test('encodes frame at exactly 65535 bytes (max 16-bit)', () => {
301
+ const payload = Buffer.alloc(65535, 0x45);
302
+ const frame = ws.encodeFrame(ws.OPCODES.TEXT, payload);
303
+ assert.strictEqual(frame[1], 126);
304
+ assert.strictEqual(frame.readUInt16BE(2), 65535);
305
+ assert.strictEqual(frame.length, 4 + 65535);
306
+ });
307
+
308
+ test('encodes frame at exactly 65536 bytes (min 64-bit)', () => {
309
+ const payload = Buffer.alloc(65536, 0x46);
310
+ const frame = ws.encodeFrame(ws.OPCODES.TEXT, payload);
311
+ assert.strictEqual(frame[1], 127);
312
+ assert.strictEqual(Number(frame.readBigUInt64BE(2)), 65536);
313
+ assert.strictEqual(frame.length, 10 + 65536);
314
+ });
315
+
316
+ test('decodes frame at 65535 bytes boundary', () => {
317
+ const payload = 'X'.repeat(65535);
318
+ const frame = makeClientFrame(0x01, payload);
319
+ const result = ws.decodeFrame(frame);
320
+ assert(result);
321
+ assert.strictEqual(result.payload.length, 65535);
322
+ });
323
+
324
+ test('decodes frame at 65536 bytes boundary', () => {
325
+ const payload = 'Y'.repeat(65536);
326
+ const frame = makeClientFrame(0x01, payload);
327
+ const result = ws.decodeFrame(frame);
328
+ assert(result);
329
+ assert.strictEqual(result.payload.length, 65536);
330
+ });
331
+
332
+ // ========== Close Frame with Status Code ==========
333
+ console.log('\n--- Close Frame Details ---');
334
+
335
+ test('decodes close frame with status code', () => {
336
+ // Close frame payload: 2-byte status code + optional reason
337
+ const statusBuf = Buffer.alloc(2);
338
+ statusBuf.writeUInt16BE(1000); // Normal closure
339
+ const frame = makeClientFrame(0x08, statusBuf);
340
+ const result = ws.decodeFrame(frame);
341
+ assert.strictEqual(result.opcode, ws.OPCODES.CLOSE);
342
+ assert.strictEqual(result.payload.readUInt16BE(0), 1000);
343
+ });
344
+
345
+ test('decodes close frame with status code and reason', () => {
346
+ const reason = 'Normal shutdown';
347
+ const payload = Buffer.alloc(2 + reason.length);
348
+ payload.writeUInt16BE(1000);
349
+ payload.write(reason, 2);
350
+ const frame = makeClientFrame(0x08, payload);
351
+ const result = ws.decodeFrame(frame);
352
+ assert.strictEqual(result.opcode, ws.OPCODES.CLOSE);
353
+ assert.strictEqual(result.payload.slice(2).toString(), reason);
354
+ });
355
+
356
+ // ========== JSON Roundtrip ==========
357
+ console.log('\n--- JSON Message Roundtrip ---');
358
+
359
+ test('roundtrip encode/decode of JSON message', () => {
360
+ const msg = { type: 'reload' };
361
+ const payload = Buffer.from(JSON.stringify(msg));
362
+ const serverFrame = ws.encodeFrame(ws.OPCODES.TEXT, payload);
363
+
364
+ // Verify we can read what we encoded (unmasked server frame)
365
+ // Server frames don't go through decodeFrame (that expects masked),
366
+ // so just verify the payload bytes directly
367
+ let offset;
368
+ if (serverFrame[1] < 126) {
369
+ offset = 2;
370
+ } else if (serverFrame[1] === 126) {
371
+ offset = 4;
372
+ } else {
373
+ offset = 10;
374
+ }
375
+ const decoded = JSON.parse(serverFrame.slice(offset).toString());
376
+ assert.deepStrictEqual(decoded, msg);
377
+ });
378
+
379
+ test('roundtrip masked client JSON message', () => {
380
+ const msg = { type: 'click', choice: 'a', text: 'Option A', timestamp: 1706000101 };
381
+ const frame = makeClientFrame(0x01, JSON.stringify(msg));
382
+ const result = ws.decodeFrame(frame);
383
+ const decoded = JSON.parse(result.payload.toString());
384
+ assert.deepStrictEqual(decoded, msg);
385
+ });
386
+
387
+ // ========== Summary ==========
388
+ console.log(`\n--- Results: ${passed} passed, ${failed} failed ---`);
389
+ if (failed > 0) process.exit(1);
390
+ }
391
+
392
+ runTests();
@@ -0,0 +1,158 @@
1
+ # Claude Code Skills Tests
2
+
3
+ Automated tests for superpowers skills using Claude Code CLI.
4
+
5
+ ## Overview
6
+
7
+ This test suite verifies that skills are loaded correctly and Claude follows them as expected. Tests invoke Claude Code in headless mode (`claude -p`) and verify the behavior.
8
+
9
+ ## Requirements
10
+
11
+ - Claude Code CLI installed and in PATH (`claude --version` should work)
12
+ - Local superpowers plugin installed (see main README for installation)
13
+
14
+ ## Running Tests
15
+
16
+ ### Run all fast tests (recommended):
17
+ ```bash
18
+ ./run-skill-tests.sh
19
+ ```
20
+
21
+ ### Run integration tests (slow, 10-30 minutes):
22
+ ```bash
23
+ ./run-skill-tests.sh --integration
24
+ ```
25
+
26
+ ### Run specific test:
27
+ ```bash
28
+ ./run-skill-tests.sh --test test-subagent-driven-development.sh
29
+ ```
30
+
31
+ ### Run with verbose output:
32
+ ```bash
33
+ ./run-skill-tests.sh --verbose
34
+ ```
35
+
36
+ ### Set custom timeout:
37
+ ```bash
38
+ ./run-skill-tests.sh --timeout 1800 # 30 minutes for integration tests
39
+ ```
40
+
41
+ ## Test Structure
42
+
43
+ ### test-helpers.sh
44
+ Common functions for skills testing:
45
+ - `run_claude "prompt" [timeout]` - Run Claude with prompt
46
+ - `assert_contains output pattern name` - Verify pattern exists
47
+ - `assert_not_contains output pattern name` - Verify pattern absent
48
+ - `assert_count output pattern count name` - Verify exact count
49
+ - `assert_order output pattern_a pattern_b name` - Verify order
50
+ - `create_test_project` - Create temp test directory
51
+ - `create_test_plan project_dir` - Create sample plan file
52
+
53
+ ### Test Files
54
+
55
+ Each test file:
56
+ 1. Sources `test-helpers.sh`
57
+ 2. Runs Claude Code with specific prompts
58
+ 3. Verifies expected behavior using assertions
59
+ 4. Returns 0 on success, non-zero on failure
60
+
61
+ ## Example Test
62
+
63
+ ```bash
64
+ #!/usr/bin/env bash
65
+ set -euo pipefail
66
+
67
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
68
+ source "$SCRIPT_DIR/test-helpers.sh"
69
+
70
+ echo "=== Test: My Skill ==="
71
+
72
+ # Ask Claude about the skill
73
+ output=$(run_claude "What does the my-skill skill do?" 30)
74
+
75
+ # Verify response
76
+ assert_contains "$output" "expected behavior" "Skill describes behavior"
77
+
78
+ echo "=== All tests passed ==="
79
+ ```
80
+
81
+ ## Current Tests
82
+
83
+ ### Fast Tests (run by default)
84
+
85
+ #### test-subagent-driven-development.sh
86
+ Tests skill content and requirements (~2 minutes):
87
+ - Skill loading and accessibility
88
+ - Workflow ordering (spec compliance before code quality)
89
+ - Self-review requirements documented
90
+ - Plan reading efficiency documented
91
+ - Spec compliance reviewer skepticism documented
92
+ - Review loops documented
93
+ - Task context provision documented
94
+
95
+ ### Integration Tests (use --integration flag)
96
+
97
+ #### test-subagent-driven-development-integration.sh
98
+ Full workflow execution test (~10-30 minutes):
99
+ - Creates real test project with Node.js setup
100
+ - Creates implementation plan with 2 tasks
101
+ - Executes plan using subagent-driven-development
102
+ - Verifies actual behaviors:
103
+ - Plan read once at start (not per task)
104
+ - Full task text provided in subagent prompts
105
+ - Subagents perform self-review before reporting
106
+ - Spec compliance review happens before code quality
107
+ - Spec reviewer reads code independently
108
+ - Working implementation is produced
109
+ - Tests pass
110
+ - Proper git commits created
111
+
112
+ **What it tests:**
113
+ - The workflow actually works end-to-end
114
+ - Our improvements are actually applied
115
+ - Subagents follow the skill correctly
116
+ - Final code is functional and tested
117
+
118
+ ## Adding New Tests
119
+
120
+ 1. Create new test file: `test-<skill-name>.sh`
121
+ 2. Source test-helpers.sh
122
+ 3. Write tests using `run_claude` and assertions
123
+ 4. Add to test list in `run-skill-tests.sh`
124
+ 5. Make executable: `chmod +x test-<skill-name>.sh`
125
+
126
+ ## Timeout Considerations
127
+
128
+ - Default timeout: 5 minutes per test
129
+ - Claude Code may take time to respond
130
+ - Adjust with `--timeout` if needed
131
+ - Tests should be focused to avoid long runs
132
+
133
+ ## Debugging Failed Tests
134
+
135
+ With `--verbose`, you'll see full Claude output:
136
+ ```bash
137
+ ./run-skill-tests.sh --verbose --test test-subagent-driven-development.sh
138
+ ```
139
+
140
+ Without verbose, only failures show output.
141
+
142
+ ## CI/CD Integration
143
+
144
+ To run in CI:
145
+ ```bash
146
+ # Run with explicit timeout for CI environments
147
+ ./run-skill-tests.sh --timeout 900
148
+
149
+ # Exit code 0 = success, non-zero = failure
150
+ ```
151
+
152
+ ## Notes
153
+
154
+ - Tests verify skill *instructions*, not full execution
155
+ - Full workflow tests would be very slow
156
+ - Focus on verifying key skill requirements
157
+ - Tests should be deterministic
158
+ - Avoid testing implementation details