@hustle-together/api-dev-tools 3.6.5 → 3.10.0

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 (72) hide show
  1. package/README.md +5599 -258
  2. package/bin/cli.js +395 -20
  3. package/commands/README.md +459 -71
  4. package/commands/hustle-api-continue.md +158 -0
  5. package/commands/{api-create.md → hustle-api-create.md} +35 -15
  6. package/commands/{api-env.md → hustle-api-env.md} +4 -4
  7. package/commands/{api-interview.md → hustle-api-interview.md} +1 -1
  8. package/commands/{api-research.md → hustle-api-research.md} +3 -3
  9. package/commands/hustle-api-sessions.md +149 -0
  10. package/commands/{api-status.md → hustle-api-status.md} +16 -16
  11. package/commands/{api-verify.md → hustle-api-verify.md} +2 -2
  12. package/commands/hustle-combine.md +763 -0
  13. package/commands/hustle-ui-create-page.md +933 -0
  14. package/commands/hustle-ui-create.md +825 -0
  15. package/hooks/api-workflow-check.py +545 -21
  16. package/hooks/cache-research.py +337 -0
  17. package/hooks/check-api-routes.py +168 -0
  18. package/hooks/check-playwright-setup.py +103 -0
  19. package/hooks/check-storybook-setup.py +81 -0
  20. package/hooks/detect-interruption.py +165 -0
  21. package/hooks/enforce-a11y-audit.py +202 -0
  22. package/hooks/enforce-brand-guide.py +241 -0
  23. package/hooks/enforce-documentation.py +60 -8
  24. package/hooks/enforce-freshness.py +184 -0
  25. package/hooks/enforce-page-components.py +186 -0
  26. package/hooks/enforce-page-data-schema.py +155 -0
  27. package/hooks/enforce-questions-sourced.py +146 -0
  28. package/hooks/enforce-schema-from-interview.py +248 -0
  29. package/hooks/enforce-ui-disambiguation.py +108 -0
  30. package/hooks/enforce-ui-interview.py +130 -0
  31. package/hooks/generate-manifest-entry.py +1161 -0
  32. package/hooks/session-logger.py +297 -0
  33. package/hooks/session-startup.py +160 -15
  34. package/hooks/track-scope-coverage.py +220 -0
  35. package/hooks/track-tool-use.py +81 -1
  36. package/hooks/update-api-showcase.py +149 -0
  37. package/hooks/update-registry.py +352 -0
  38. package/hooks/update-ui-showcase.py +212 -0
  39. package/package.json +8 -3
  40. package/templates/BRAND_GUIDE.md +299 -0
  41. package/templates/CLAUDE-SECTION.md +56 -24
  42. package/templates/SPEC.json +640 -0
  43. package/templates/api-dev-state.json +217 -161
  44. package/templates/api-showcase/_components/APICard.tsx +153 -0
  45. package/templates/api-showcase/_components/APIModal.tsx +375 -0
  46. package/templates/api-showcase/_components/APIShowcase.tsx +231 -0
  47. package/templates/api-showcase/_components/APITester.tsx +522 -0
  48. package/templates/api-showcase/page.tsx +41 -0
  49. package/templates/component/Component.stories.tsx +172 -0
  50. package/templates/component/Component.test.tsx +237 -0
  51. package/templates/component/Component.tsx +86 -0
  52. package/templates/component/Component.types.ts +55 -0
  53. package/templates/component/index.ts +15 -0
  54. package/templates/dev-tools/_components/DevToolsLanding.tsx +320 -0
  55. package/templates/dev-tools/page.tsx +10 -0
  56. package/templates/page/page.e2e.test.ts +218 -0
  57. package/templates/page/page.tsx +42 -0
  58. package/templates/performance-budgets.json +58 -0
  59. package/templates/registry.json +13 -0
  60. package/templates/settings.json +90 -0
  61. package/templates/shared/HeroHeader.tsx +261 -0
  62. package/templates/shared/index.ts +1 -0
  63. package/templates/ui-showcase/_components/PreviewCard.tsx +315 -0
  64. package/templates/ui-showcase/_components/PreviewModal.tsx +676 -0
  65. package/templates/ui-showcase/_components/UIShowcase.tsx +262 -0
  66. package/templates/ui-showcase/page.tsx +26 -0
  67. package/demo/hustle-together/blog/gemini-vs-claude-widgets.html +0 -959
  68. package/demo/hustle-together/blog/interview-driven-api-development.html +0 -1146
  69. package/demo/hustle-together/blog/tdd-for-ai.html +0 -982
  70. package/demo/hustle-together/index.html +0 -1312
  71. package/demo/workflow-demo-v3.5-backup.html +0 -5008
  72. package/demo/workflow-demo.html +0 -6202
@@ -1,1312 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Hustle Together - Tools for Builders</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com">
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Righteous&family=Bungee&family=Fredoka:wght@600;700&display=swap" rel="stylesheet">
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.14.1/gsap.min.js"></script>
11
- <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.14.1/ScrollTrigger.min.js"></script>
12
- <style>
13
- /* ============================================
14
- HUSTLE TOGETHER - 90s BOXY AESTHETIC
15
- Light/Dark Mode Support
16
- ============================================ */
17
-
18
- * {
19
- margin: 0;
20
- padding: 0;
21
- box-sizing: border-box;
22
- }
23
-
24
- :root {
25
- /* Dark Mode (default) - Bulls/Nike colorway: Red, White, Black */
26
- --bg: #0a0a0a;
27
- --bg-secondary: #121212;
28
- --bg-card: #1a1a1a;
29
- --text: #e8e8e8;
30
- --text-muted: #999;
31
- --text-dim: #555;
32
- --accent: #fff;
33
- --accent-red: #BA0C2F;
34
- --accent-red-glow: rgba(186, 12, 47, 0.4);
35
- --border: #333;
36
- --border-strong: #444;
37
- --glow: rgba(255, 255, 255, 0.3);
38
- --pattern-opacity: 0.03;
39
- }
40
-
41
- [data-theme="light"] {
42
- --bg: #f0f0f0;
43
- --bg-secondary: #fff;
44
- --bg-card: #fafafa;
45
- --text: #1a1a1a;
46
- --text-muted: #666;
47
- --text-dim: #aaa;
48
- --accent: #000;
49
- --accent-red: #BA0C2F;
50
- --accent-red-glow: rgba(186, 12, 47, 0.3);
51
- --border: #ddd;
52
- --border-strong: #ccc;
53
- --glow: rgba(0, 0, 0, 0.15);
54
- --pattern-opacity: 0.05;
55
- }
56
-
57
- body {
58
- background: var(--bg);
59
- color: var(--text);
60
- font-family: 'JetBrains Mono', 'Courier New', monospace;
61
- line-height: 1.7;
62
- overflow-x: hidden;
63
- transition: background 0.3s, color 0.3s;
64
- }
65
-
66
- /* Animated Background Pattern */
67
- .bg-pattern {
68
- position: fixed;
69
- top: 0;
70
- left: 0;
71
- width: 100%;
72
- height: 100%;
73
- pointer-events: none;
74
- z-index: 0;
75
- opacity: var(--pattern-opacity);
76
- }
77
-
78
- .bg-pattern svg {
79
- width: 100%;
80
- height: 100%;
81
- }
82
-
83
- .grid-line {
84
- stroke: var(--text);
85
- stroke-width: 0.5;
86
- fill: none;
87
- }
88
-
89
- /* Navigation - clean boxy style */
90
- nav {
91
- position: fixed;
92
- top: 0;
93
- left: 0;
94
- right: 0;
95
- z-index: 1000;
96
- padding: 15px 40px;
97
- display: flex;
98
- justify-content: space-between;
99
- align-items: center;
100
- background: var(--bg);
101
- border-bottom: 2px solid var(--border);
102
- }
103
-
104
- .logo {
105
- font-size: 1.1rem;
106
- font-family: 'Fredoka', 'Bungee', sans-serif;
107
- font-weight: 700;
108
- letter-spacing: 2px;
109
- display: flex;
110
- align-items: center;
111
- gap: 10px;
112
- }
113
-
114
- .logo-icon {
115
- width: 32px;
116
- height: 32px;
117
- border: 2px solid var(--accent-red);
118
- background: var(--accent-red);
119
- color: var(--bg);
120
- display: flex;
121
- align-items: center;
122
- justify-content: center;
123
- font-size: 0.85rem;
124
- font-weight: bold;
125
- }
126
-
127
- .nav-links {
128
- display: flex;
129
- gap: 25px;
130
- align-items: center;
131
- }
132
-
133
- .nav-links a {
134
- color: var(--text-muted);
135
- text-decoration: none;
136
- font-size: 0.8rem;
137
- letter-spacing: 1px;
138
- text-transform: uppercase;
139
- transition: all 0.2s;
140
- padding: 8px 12px;
141
- border: 2px solid transparent;
142
- }
143
-
144
- .nav-links a:hover {
145
- color: var(--accent-red);
146
- border-color: var(--accent-red);
147
- }
148
-
149
- .theme-toggle {
150
- background: var(--bg-card);
151
- border: 2px solid var(--border);
152
- color: var(--text);
153
- padding: 8px 14px;
154
- cursor: pointer;
155
- font-family: inherit;
156
- font-size: 0.75rem;
157
- transition: all 0.2s;
158
- text-transform: uppercase;
159
- letter-spacing: 1px;
160
- }
161
-
162
- .theme-toggle:hover {
163
- background: var(--accent-red);
164
- color: #fff;
165
- border-color: var(--accent-red);
166
- }
167
-
168
- /* Main Content */
169
- main {
170
- position: relative;
171
- z-index: 1;
172
- }
173
-
174
- section {
175
- min-height: 100vh;
176
- display: flex;
177
- flex-direction: column;
178
- justify-content: center;
179
- align-items: center;
180
- padding: 120px 40px 80px;
181
- position: relative;
182
- }
183
-
184
- /* Hero Section */
185
- #hero {
186
- text-align: center;
187
- }
188
-
189
- .hero-content {
190
- max-width: 800px;
191
- }
192
-
193
- .hero-badge {
194
- display: inline-block;
195
- border: 2px solid var(--border);
196
- background: var(--bg-card);
197
- padding: 10px 25px;
198
- font-size: 0.7rem;
199
- letter-spacing: 3px;
200
- color: var(--text-muted);
201
- margin-bottom: 40px;
202
- opacity: 0;
203
- text-transform: uppercase;
204
- }
205
-
206
- h1 {
207
- font-size: 6.5rem;
208
- font-family: 'Fredoka', 'Bungee', sans-serif;
209
- font-weight: 700;
210
- letter-spacing: 4px;
211
- margin-bottom: 10px;
212
- display: flex;
213
- flex-wrap: wrap;
214
- justify-content: center;
215
- gap: 0 20px;
216
- line-height: 1;
217
- }
218
-
219
- h1 .word {
220
- display: flex;
221
- }
222
-
223
- h1 .letter {
224
- display: inline-block;
225
- opacity: 0;
226
- transform: translateY(50px) rotateX(-90deg);
227
- }
228
-
229
- h1 .highlight {
230
- font-weight: 700;
231
- color: var(--accent);
232
- }
233
-
234
- .hero-tagline {
235
- font-size: 1.3rem;
236
- color: var(--text-muted);
237
- margin-top: 30px;
238
- letter-spacing: 2px;
239
- opacity: 0;
240
- }
241
-
242
- .hero-desc {
243
- margin-top: 40px;
244
- font-size: 1rem;
245
- color: var(--text-muted);
246
- line-height: 2;
247
- max-width: 600px;
248
- margin-left: auto;
249
- margin-right: auto;
250
- opacity: 0;
251
- }
252
-
253
- .hero-cta {
254
- margin-top: 50px;
255
- display: flex;
256
- gap: 20px;
257
- justify-content: center;
258
- opacity: 0;
259
- }
260
-
261
- .btn {
262
- display: inline-block;
263
- padding: 15px 40px;
264
- border: 2px solid var(--text);
265
- color: var(--text);
266
- text-decoration: none;
267
- font-size: 0.85rem;
268
- letter-spacing: 2px;
269
- text-transform: uppercase;
270
- transition: all 0.2s;
271
- background: transparent;
272
- cursor: pointer;
273
- font-family: inherit;
274
- }
275
-
276
- .btn:hover {
277
- background: var(--text);
278
- color: var(--bg);
279
- }
280
-
281
- .btn-primary {
282
- background: var(--accent-red);
283
- color: #fff;
284
- border-color: var(--accent-red);
285
- }
286
-
287
- .btn-primary:hover {
288
- background: #fff;
289
- color: var(--accent-red);
290
- border-color: var(--accent-red);
291
- }
292
-
293
- /* Section Headers */
294
- .section-header {
295
- text-align: center;
296
- margin-bottom: 60px;
297
- }
298
-
299
- .section-label {
300
- font-size: 0.75rem;
301
- letter-spacing: 4px;
302
- color: var(--text-muted);
303
- margin-bottom: 15px;
304
- }
305
-
306
- h2 {
307
- font-size: 2.5rem;
308
- font-family: 'Fredoka', 'Bungee', sans-serif;
309
- font-weight: 600;
310
- letter-spacing: 3px;
311
- }
312
-
313
- h2 .highlight {
314
- font-weight: 700;
315
- color: var(--accent);
316
- }
317
-
318
- /* Philosophy Section - boxy cards */
319
- #philosophy {
320
- background: var(--bg-secondary);
321
- }
322
-
323
- .philosophy-grid {
324
- display: grid;
325
- grid-template-columns: repeat(3, 1fr);
326
- gap: 25px;
327
- max-width: 1000px;
328
- }
329
-
330
- .philosophy-card {
331
- border: 2px solid var(--border);
332
- background: var(--bg-card);
333
- padding: 40px 30px;
334
- text-align: center;
335
- transition: all 0.2s;
336
- opacity: 0;
337
- transform: translateY(20px);
338
- }
339
-
340
- .philosophy-card:hover {
341
- border-color: var(--accent-red);
342
- transform: translateY(-3px);
343
- }
344
-
345
- .philosophy-icon {
346
- width: 55px;
347
- height: 55px;
348
- border: 2px solid var(--accent-red);
349
- background: var(--accent-red);
350
- color: #fff;
351
- margin: 0 auto 25px;
352
- display: flex;
353
- align-items: center;
354
- justify-content: center;
355
- font-size: 1.3rem;
356
- }
357
-
358
- .philosophy-title {
359
- font-size: 1rem;
360
- letter-spacing: 2px;
361
- margin-bottom: 15px;
362
- text-transform: uppercase;
363
- }
364
-
365
- .philosophy-desc {
366
- font-size: 0.85rem;
367
- color: var(--text-muted);
368
- line-height: 1.8;
369
- }
370
-
371
- /* Tools Section - boxy cards */
372
- .tools-grid {
373
- display: grid;
374
- grid-template-columns: repeat(2, 1fr);
375
- gap: 30px;
376
- max-width: 1000px;
377
- width: 100%;
378
- }
379
-
380
- .tool-card {
381
- border: 2px solid var(--border);
382
- background: var(--bg-card);
383
- padding: 40px;
384
- transition: all 0.2s;
385
- opacity: 0;
386
- transform: translateY(20px);
387
- position: relative;
388
- overflow: hidden;
389
- }
390
-
391
- .tool-card::before {
392
- content: '';
393
- position: absolute;
394
- top: 0;
395
- left: 0;
396
- width: 4px;
397
- height: 100%;
398
- background: var(--accent-red);
399
- transition: width 0.2s;
400
- }
401
-
402
- .tool-card:hover {
403
- border-color: var(--accent-red);
404
- }
405
-
406
- .tool-card:hover::before {
407
- width: 100%;
408
- opacity: 0.1;
409
- }
410
-
411
- .tool-badge {
412
- display: inline-block;
413
- font-size: 0.65rem;
414
- letter-spacing: 2px;
415
- color: var(--accent-red);
416
- border: 2px solid var(--accent-red);
417
- padding: 6px 14px;
418
- margin-bottom: 20px;
419
- text-transform: uppercase;
420
- font-weight: bold;
421
- }
422
-
423
- .tool-name {
424
- font-size: 1.4rem;
425
- letter-spacing: 2px;
426
- margin-bottom: 15px;
427
- display: flex;
428
- align-items: center;
429
- gap: 15px;
430
- }
431
-
432
- .tool-name .icon {
433
- width: 40px;
434
- height: 40px;
435
- border: 2px solid var(--accent-red);
436
- background: var(--accent-red);
437
- color: #fff;
438
- display: flex;
439
- align-items: center;
440
- justify-content: center;
441
- font-size: 1rem;
442
- }
443
-
444
- .tool-desc {
445
- color: var(--text-muted);
446
- font-size: 0.9rem;
447
- line-height: 1.8;
448
- margin-bottom: 25px;
449
- }
450
-
451
- .tool-features {
452
- list-style: none;
453
- margin-bottom: 30px;
454
- }
455
-
456
- .tool-features li {
457
- font-size: 0.8rem;
458
- color: var(--text-muted);
459
- padding: 10px 0;
460
- border-bottom: 1px solid var(--border);
461
- display: flex;
462
- align-items: center;
463
- gap: 10px;
464
- }
465
-
466
- .tool-features li::before {
467
- content: '+';
468
- color: var(--accent-red);
469
- font-weight: bold;
470
- }
471
-
472
- .tool-links {
473
- display: flex;
474
- gap: 12px;
475
- }
476
-
477
- .tool-link {
478
- font-size: 0.75rem;
479
- color: var(--text);
480
- text-decoration: none;
481
- border: 2px solid var(--border);
482
- padding: 10px 20px;
483
- transition: all 0.2s;
484
- text-transform: uppercase;
485
- letter-spacing: 1px;
486
- }
487
-
488
- .tool-link:hover {
489
- background: var(--accent-red);
490
- color: #fff;
491
- border-color: var(--accent-red);
492
- }
493
-
494
- /* Shop Section - boxy style */
495
- #shop {
496
- background: var(--bg-secondary);
497
- }
498
-
499
- .shop-preview {
500
- max-width: 800px;
501
- text-align: center;
502
- }
503
-
504
- .coming-soon-box {
505
- border: 2px solid var(--border);
506
- background: var(--bg-card);
507
- padding: 80px 60px;
508
- margin-top: 40px;
509
- position: relative;
510
- opacity: 0;
511
- }
512
-
513
- .coming-soon-box::before {
514
- content: 'COMING SOON';
515
- position: absolute;
516
- top: -12px;
517
- left: 50%;
518
- transform: translateX(-50%);
519
- background: var(--accent-red);
520
- color: #fff;
521
- padding: 5px 20px;
522
- font-size: 0.7rem;
523
- letter-spacing: 3px;
524
- }
525
-
526
- .merch-grid {
527
- display: grid;
528
- grid-template-columns: repeat(3, 1fr);
529
- gap: 25px;
530
- margin-top: 40px;
531
- }
532
-
533
- .merch-placeholder {
534
- aspect-ratio: 1;
535
- border: 2px solid var(--border);
536
- background: var(--bg);
537
- display: flex;
538
- flex-direction: column;
539
- align-items: center;
540
- justify-content: center;
541
- color: var(--text-dim);
542
- font-size: 0.75rem;
543
- transition: all 0.2s;
544
- }
545
-
546
- .merch-placeholder:hover {
547
- border-color: var(--accent-red);
548
- }
549
-
550
- .merch-placeholder .icon {
551
- font-size: 2rem;
552
- margin-bottom: 15px;
553
- }
554
-
555
- .shop-notify {
556
- margin-top: 50px;
557
- }
558
-
559
- .email-form {
560
- display: flex;
561
- gap: 10px;
562
- justify-content: center;
563
- max-width: 400px;
564
- margin: 20px auto 0;
565
- }
566
-
567
- .email-input {
568
- flex: 1;
569
- padding: 12px 20px;
570
- border: 2px solid var(--border);
571
- background: var(--bg);
572
- color: var(--text);
573
- font-family: inherit;
574
- font-size: 0.85rem;
575
- }
576
-
577
- .email-input::placeholder {
578
- color: var(--text-dim);
579
- }
580
-
581
- .email-input:focus {
582
- outline: none;
583
- border-color: var(--accent-red);
584
- }
585
-
586
- /* Blog Section - boxy cards */
587
- .blog-grid {
588
- display: grid;
589
- grid-template-columns: repeat(3, 1fr);
590
- gap: 25px;
591
- max-width: 1100px;
592
- width: 100%;
593
- }
594
-
595
- .blog-card {
596
- border: 2px solid var(--border);
597
- background: var(--bg-card);
598
- transition: all 0.2s;
599
- opacity: 0;
600
- transform: translateY(20px);
601
- overflow: hidden;
602
- }
603
-
604
- .blog-card:hover {
605
- border-color: var(--accent-red);
606
- }
607
-
608
- .blog-card-image {
609
- width: 100%;
610
- aspect-ratio: 16/9;
611
- background: var(--bg);
612
- border-bottom: 2px solid var(--border);
613
- display: flex;
614
- align-items: center;
615
- justify-content: center;
616
- font-size: 3rem;
617
- color: var(--text-dim);
618
- text-decoration: none;
619
- transition: all 0.2s;
620
- }
621
-
622
- .blog-card-image:hover {
623
- background: var(--accent-red);
624
- color: #fff;
625
- }
626
-
627
- .blog-card-content {
628
- padding: 25px;
629
- }
630
-
631
- .blog-card-meta {
632
- display: flex;
633
- gap: 15px;
634
- margin-bottom: 15px;
635
- }
636
-
637
- .blog-tag {
638
- font-size: 0.6rem;
639
- letter-spacing: 2px;
640
- color: #fff;
641
- background: var(--accent-red);
642
- border: 2px solid var(--accent-red);
643
- padding: 4px 12px;
644
- text-transform: uppercase;
645
- font-weight: bold;
646
- }
647
-
648
- .blog-date {
649
- font-size: 0.75rem;
650
- color: var(--text-dim);
651
- }
652
-
653
- .blog-card-title {
654
- font-size: 1.1rem;
655
- font-family: 'Fredoka', sans-serif;
656
- font-weight: 600;
657
- margin-bottom: 12px;
658
- line-height: 1.4;
659
- }
660
-
661
- .blog-card-excerpt {
662
- font-size: 0.85rem;
663
- color: var(--text-muted);
664
- line-height: 1.7;
665
- margin-bottom: 20px;
666
- }
667
-
668
- .blog-card-link {
669
- font-size: 0.8rem;
670
- color: var(--accent-red);
671
- text-decoration: none;
672
- display: inline-flex;
673
- align-items: center;
674
- gap: 8px;
675
- transition: all 0.3s;
676
- }
677
-
678
- .blog-card-link:hover {
679
- gap: 15px;
680
- }
681
-
682
- .blog-card-link::after {
683
- content: '→';
684
- color: var(--accent-red);
685
- }
686
-
687
- .blog-featured {
688
- grid-column: span 2;
689
- }
690
-
691
- .blog-featured .blog-card-image {
692
- aspect-ratio: 21/9;
693
- }
694
-
695
- .blog-featured .blog-card-title {
696
- font-size: 1.4rem;
697
- }
698
-
699
- .blog-cta {
700
- margin-top: 50px;
701
- text-align: center;
702
- }
703
-
704
- @media (max-width: 900px) {
705
- .blog-grid {
706
- grid-template-columns: 1fr;
707
- }
708
-
709
- .blog-featured {
710
- grid-column: span 1;
711
- }
712
-
713
- .blog-featured .blog-card-image {
714
- aspect-ratio: 16/9;
715
- }
716
- }
717
-
718
- /* Footer - boxy style */
719
- footer {
720
- border-top: 2px solid var(--border);
721
- padding: 60px 40px;
722
- text-align: center;
723
- background: var(--bg-secondary);
724
- }
725
-
726
- .footer-logo {
727
- font-size: 1.4rem;
728
- font-family: 'Fredoka', 'Bungee', sans-serif;
729
- font-weight: 700;
730
- letter-spacing: 3px;
731
- margin-bottom: 30px;
732
- }
733
-
734
- .footer-links {
735
- display: flex;
736
- gap: 25px;
737
- justify-content: center;
738
- margin-bottom: 30px;
739
- }
740
-
741
- .footer-links a {
742
- color: var(--text-muted);
743
- text-decoration: none;
744
- font-size: 0.8rem;
745
- letter-spacing: 1px;
746
- text-transform: uppercase;
747
- padding: 8px 12px;
748
- border: 2px solid transparent;
749
- transition: all 0.2s;
750
- }
751
-
752
- .footer-links a:hover {
753
- color: var(--accent-red);
754
- border-color: var(--accent-red);
755
- }
756
-
757
- .footer-copy {
758
- color: var(--text-dim);
759
- font-size: 0.75rem;
760
- }
761
-
762
- /* Animated Elements */
763
- .float-element {
764
- position: absolute;
765
- border: 2px solid var(--border);
766
- opacity: 0.3;
767
- pointer-events: none;
768
- }
769
-
770
- /* ASCII Art Decorations */
771
- .ascii-corner {
772
- position: absolute;
773
- color: var(--text-dim);
774
- font-size: 0.7rem;
775
- pointer-events: none;
776
- }
777
-
778
- .ascii-corner.top-left {
779
- top: 20px;
780
- left: 20px;
781
- }
782
-
783
- .ascii-corner.bottom-right {
784
- bottom: 20px;
785
- right: 20px;
786
- }
787
-
788
- /* Responsive */
789
- @media (max-width: 900px) {
790
- h1 {
791
- font-size: 4rem;
792
- letter-spacing: 2px;
793
- gap: 0 12px;
794
- }
795
-
796
- .philosophy-grid {
797
- grid-template-columns: 1fr;
798
- }
799
-
800
- .tools-grid {
801
- grid-template-columns: 1fr;
802
- }
803
-
804
- .merch-grid {
805
- grid-template-columns: repeat(2, 1fr);
806
- }
807
-
808
- nav {
809
- padding: 15px 20px;
810
- }
811
-
812
- .nav-links {
813
- gap: 15px;
814
- }
815
-
816
- .nav-links a {
817
- font-size: 0.75rem;
818
- }
819
- }
820
-
821
- @media (max-width: 600px) {
822
- .nav-links {
823
- display: none;
824
- }
825
-
826
- .hero-cta {
827
- flex-direction: column;
828
- align-items: center;
829
- }
830
-
831
- .merch-grid {
832
- grid-template-columns: 1fr;
833
- }
834
-
835
- .email-form {
836
- flex-direction: column;
837
- }
838
- }
839
-
840
- /* Cursor effect */
841
- .cursor-glow {
842
- position: fixed;
843
- width: 200px;
844
- height: 200px;
845
- border-radius: 50%;
846
- background: radial-gradient(circle, var(--glow) 0%, transparent 70%);
847
- pointer-events: none;
848
- z-index: 9999;
849
- opacity: 0.5;
850
- transform: translate(-50%, -50%);
851
- transition: opacity 0.3s;
852
- }
853
-
854
- /* Scroll indicator */
855
- .scroll-indicator {
856
- position: absolute;
857
- bottom: 40px;
858
- left: 50%;
859
- transform: translateX(-50%);
860
- color: var(--text-muted);
861
- font-size: 0.75rem;
862
- letter-spacing: 2px;
863
- animation: bounce 2s infinite;
864
- opacity: 0;
865
- }
866
-
867
- @keyframes bounce {
868
- 0%, 100% { transform: translateX(-50%) translateY(0); }
869
- 50% { transform: translateX(-50%) translateY(10px); }
870
- }
871
- </style>
872
- </head>
873
- <body>
874
- <!-- Animated Background Pattern -->
875
- <div class="bg-pattern">
876
- <svg id="gridPattern" xmlns="http://www.w3.org/2000/svg">
877
- <defs>
878
- <pattern id="grid" width="50" height="50" patternUnits="userSpaceOnUse">
879
- <path d="M 50 0 L 0 0 0 50" fill="none" class="grid-line"/>
880
- </pattern>
881
- </defs>
882
- <rect width="100%" height="100%" fill="url(#grid)"/>
883
- </svg>
884
- </div>
885
-
886
- <!-- Cursor Glow Effect -->
887
- <div class="cursor-glow" id="cursorGlow"></div>
888
-
889
- <!-- Navigation -->
890
- <nav>
891
- <div class="logo">
892
- <div class="logo-icon">HT</div>
893
- HUSTLE TOGETHER
894
- </div>
895
- <div class="nav-links">
896
- <a href="#hero">HOME</a>
897
- <a href="#philosophy">PHILOSOPHY</a>
898
- <a href="#tools">TOOLS</a>
899
- <a href="#blog">BLOG</a>
900
- <a href="#shop">SHOP</a>
901
- <button class="theme-toggle" id="themeToggle">[ LIGHT ]</button>
902
- </div>
903
- </nav>
904
-
905
- <main>
906
- <!-- Hero Section -->
907
- <section id="hero">
908
- <div class="ascii-corner top-left">+--[HUSTLE]--+</div>
909
- <div class="ascii-corner bottom-right">+--[2025]--+</div>
910
-
911
- <div class="hero-content">
912
- <div class="hero-badge">EXPERIMENT . BUILD . SHARE</div>
913
- <h1 id="heroTitle">
914
- <span class="word" data-text="HUSTLE"></span>
915
- <span class="word highlight" data-text="TOGETHER"></span>
916
- </h1>
917
- <p class="hero-tagline">Build together. Share resources. Grow stronger.</p>
918
- <p class="hero-desc">
919
- We hustle hard to create tools worth sharing. When we build together
920
- and share what we learn, everyone gets stronger. No gatekeeping - just
921
- useful stuff for developers who believe in community over competition.
922
- </p>
923
- <div class="hero-cta">
924
- <a href="#tools" class="btn btn-primary">VIEW TOOLS</a>
925
- <a href="#philosophy" class="btn">OUR PHILOSOPHY</a>
926
- </div>
927
- </div>
928
-
929
- <div class="scroll-indicator">[ SCROLL ]</div>
930
- </section>
931
-
932
- <!-- Philosophy Section -->
933
- <section id="philosophy">
934
- <div class="section-header">
935
- <div class="section-label">HOW WE WORK</div>
936
- <h2>HUSTLE <span class="highlight">PHILOSOPHY</span></h2>
937
- </div>
938
-
939
- <div class="philosophy-grid">
940
- <div class="philosophy-card">
941
- <div class="philosophy-icon">[?]</div>
942
- <div class="philosophy-title">EXPERIMENT</div>
943
- <div class="philosophy-desc">
944
- Try things before they're ready. Break stuff. Learn fast.
945
- The best tools come from real problems, not perfect plans.
946
- </div>
947
- </div>
948
- <div class="philosophy-card">
949
- <div class="philosophy-icon">[*]</div>
950
- <div class="philosophy-title">BUILD</div>
951
- <div class="philosophy-desc">
952
- Ship early, ship often. A working prototype beats a
953
- perfect spec every time. Code is the best documentation.
954
- </div>
955
- </div>
956
- <div class="philosophy-card">
957
- <div class="philosophy-icon">[>]</div>
958
- <div class="philosophy-title">SHARE</div>
959
- <div class="philosophy-desc">
960
- Open source everything. If it helped us, it might help you.
961
- The community makes tools better than we ever could alone.
962
- </div>
963
- </div>
964
- </div>
965
- </section>
966
-
967
- <!-- Tools Section -->
968
- <section id="tools">
969
- <div class="section-header">
970
- <div class="section-label">WHAT WE MAKE</div>
971
- <h2>HUSTLE <span class="highlight">TOOLS</span></h2>
972
- </div>
973
-
974
- <div class="tools-grid">
975
- <div class="tool-card">
976
- <div class="tool-badge">CLAUDE CODE EXTENSION</div>
977
- <div class="tool-name">
978
- <span class="icon">[A]</span>
979
- API DEV TOOLS
980
- </div>
981
- <p class="tool-desc">
982
- Interview-driven API development workflow for Claude Code.
983
- Enforces research, testing, and documentation through Python hooks.
984
- No more hallucinated APIs or skipped tests.
985
- </p>
986
- <ul class="tool-features">
987
- <li>Structured interviews with multiple-choice options</li>
988
- <li>Research enforcement via Context7 + WebSearch</li>
989
- <li>TDD workflow (/red, /green, /refactor)</li>
990
- <li>Decision tracking injected during implementation</li>
991
- <li>10-phase workflow with state tracking</li>
992
- </ul>
993
- <div class="tool-links">
994
- <a href="https://www.npmjs.com/package/@hustle-together/api-dev-tools" class="tool-link" target="_blank">NPM</a>
995
- <a href="https://github.com/hustle-together/api-dev-tools" class="tool-link" target="_blank">GITHUB</a>
996
- <a href="../workflow-demo.html" class="tool-link">DEMO</a>
997
- </div>
998
- </div>
999
-
1000
- <div class="tool-card">
1001
- <div class="tool-badge">WORDPRESS PLUGIN</div>
1002
- <div class="tool-name">
1003
- <span class="icon">[E]</span>
1004
- HUSTLE ELEMENTOR
1005
- </div>
1006
- <p class="tool-desc">
1007
- AI-powered widget generation for WordPress. Uses Gemini Pro 2.5
1008
- and Morph to create fully editable Elementor widgets from
1009
- natural language descriptions.
1010
- </p>
1011
- <ul class="tool-features">
1012
- <li>Gemini Pro 2.5 for intelligent generation</li>
1013
- <li>Morph integration for quick edits</li>
1014
- <li>Fully editable in WordPress admin</li>
1015
- <li>One-click widget creation</li>
1016
- <li>Style-aware generation</li>
1017
- </ul>
1018
- <div class="tool-links">
1019
- <a href="#" class="tool-link">COMING SOON</a>
1020
- </div>
1021
- </div>
1022
- </div>
1023
- </section>
1024
-
1025
- <!-- Blog Section -->
1026
- <section id="blog">
1027
- <div class="section-header">
1028
- <div class="section-label">THOUGHTS & EXPERIMENTS</div>
1029
- <h2>HUSTLE <span class="highlight">BLOG</span></h2>
1030
- </div>
1031
-
1032
- <div class="blog-grid">
1033
- <!-- Featured Post -->
1034
- <article class="blog-card blog-featured">
1035
- <a href="blog/interview-driven-api-development.html" class="blog-card-image">[*]</a>
1036
- <div class="blog-card-content">
1037
- <div class="blog-card-meta">
1038
- <span class="blog-tag">DEV TOOLS</span>
1039
- <span class="blog-date">Dec 7, 2025</span>
1040
- </div>
1041
- <h3 class="blog-card-title">Why We Built Interview-Driven API Development</h3>
1042
- <p class="blog-card-excerpt">
1043
- Claude Code is incredible, but it has a problem: it skips steps. It hallucinates APIs.
1044
- It marks things "done" without checking. We fixed that with Python hooks that enforce
1045
- a 10-phase workflow. Here's why research-first beats code-first every time.
1046
- </p>
1047
- <a href="blog/interview-driven-api-development.html" class="blog-card-link">Read More</a>
1048
- </div>
1049
- </article>
1050
-
1051
- <!-- Regular Post -->
1052
- <article class="blog-card">
1053
- <a href="blog/gemini-vs-claude-widgets.html" class="blog-card-image">[?]</a>
1054
- <div class="blog-card-content">
1055
- <div class="blog-card-meta">
1056
- <span class="blog-tag">AI</span>
1057
- <span class="blog-date">Dec 5, 2025</span>
1058
- </div>
1059
- <h3 class="blog-card-title">Gemini Pro 2.5 vs Claude for Widget Generation</h3>
1060
- <p class="blog-card-excerpt">
1061
- We tested both models for generating Elementor widgets. The results surprised us.
1062
- </p>
1063
- <a href="blog/gemini-vs-claude-widgets.html" class="blog-card-link">Read More</a>
1064
- </div>
1065
- </article>
1066
-
1067
- <!-- Regular Post -->
1068
- <article class="blog-card">
1069
- <a href="blog/tdd-for-ai.html" class="blog-card-image">[+]</a>
1070
- <div class="blog-card-content">
1071
- <div class="blog-card-meta">
1072
- <span class="blog-tag">WORKFLOW</span>
1073
- <span class="blog-date">Dec 1, 2025</span>
1074
- </div>
1075
- <h3 class="blog-card-title">TDD is Dead, Long Live TDD</h3>
1076
- <p class="blog-card-excerpt">
1077
- The /red /green /refactor cycle isn't just for humans anymore. Here's how to enforce it.
1078
- </p>
1079
- <a href="blog/tdd-for-ai.html" class="blog-card-link">Read More</a>
1080
- </div>
1081
- </article>
1082
-
1083
- <!-- Regular Post -->
1084
- <article class="blog-card">
1085
- <a href="blog/interview-driven-api-development.html" class="blog-card-image">[/]</a>
1086
- <div class="blog-card-content">
1087
- <div class="blog-card-meta">
1088
- <span class="blog-tag">OPEN SOURCE</span>
1089
- <span class="blog-date">Nov 28, 2025</span>
1090
- </div>
1091
- <h3 class="blog-card-title">Context7: The Best MCP Server You're Not Using</h3>
1092
- <p class="blog-card-excerpt">
1093
- Live documentation beats training data. Every time. Here's how to set it up.
1094
- </p>
1095
- <a href="blog/interview-driven-api-development.html" class="blog-card-link">Read More</a>
1096
- </div>
1097
- </article>
1098
- </div>
1099
-
1100
- <div class="blog-cta">
1101
- <a href="blog/interview-driven-api-development.html" class="btn">VIEW ALL POSTS</a>
1102
- </div>
1103
- </section>
1104
-
1105
- <!-- Shop Section -->
1106
- <section id="shop">
1107
- <div class="section-header">
1108
- <div class="section-label">WEAR THE HUSTLE</div>
1109
- <h2>HUSTLE <span class="highlight">MERCH</span></h2>
1110
- </div>
1111
-
1112
- <div class="shop-preview">
1113
- <div class="coming-soon-box">
1114
- <div class="merch-grid">
1115
- <div class="merch-placeholder">
1116
- <span class="icon">[T]</span>
1117
- TEE
1118
- </div>
1119
- <div class="merch-placeholder">
1120
- <span class="icon">[H]</span>
1121
- HOODIE
1122
- </div>
1123
- <div class="merch-placeholder">
1124
- <span class="icon">[C]</span>
1125
- CAP
1126
- </div>
1127
- </div>
1128
-
1129
- <div class="shop-notify">
1130
- <p style="color: var(--text-muted); font-size: 0.9rem;">
1131
- Get notified when the shop launches
1132
- </p>
1133
- <form class="email-form" onsubmit="return false;">
1134
- <input type="email" class="email-input" placeholder="your@email.com">
1135
- <button type="submit" class="btn">NOTIFY ME</button>
1136
- </form>
1137
- </div>
1138
- </div>
1139
- </div>
1140
- </section>
1141
- </main>
1142
-
1143
- <!-- Footer -->
1144
- <footer>
1145
- <div class="footer-logo">HUSTLE TOGETHER</div>
1146
- <div class="footer-links">
1147
- <a href="https://github.com/hustle-together" target="_blank">GITHUB</a>
1148
- <a href="https://www.npmjs.com/org/hustle-together" target="_blank">NPM</a>
1149
- <a href="mailto:hello@hustletogether.dev">CONTACT</a>
1150
- </div>
1151
- <div class="footer-copy">
1152
- 2025 Hustle Together. Experiment. Build. Share.
1153
- </div>
1154
- </footer>
1155
-
1156
- <script>
1157
- // Register GSAP plugins
1158
- gsap.registerPlugin(ScrollTrigger);
1159
-
1160
- // Theme Toggle
1161
- const themeToggle = document.getElementById('themeToggle');
1162
- let isDark = true;
1163
-
1164
- themeToggle.addEventListener('click', () => {
1165
- isDark = !isDark;
1166
- document.documentElement.setAttribute('data-theme', isDark ? '' : 'light');
1167
- themeToggle.textContent = isDark ? '[ LIGHT ]' : '[ DARK ]';
1168
- });
1169
-
1170
- // Cursor Glow Effect
1171
- const cursorGlow = document.getElementById('cursorGlow');
1172
- document.addEventListener('mousemove', (e) => {
1173
- cursorGlow.style.left = e.clientX + 'px';
1174
- cursorGlow.style.top = e.clientY + 'px';
1175
- });
1176
-
1177
- // Split title into letters
1178
- document.querySelectorAll('#heroTitle .word').forEach(word => {
1179
- const text = word.getAttribute('data-text');
1180
- word.innerHTML = text.split('').map(letter =>
1181
- `<span class="letter">${letter}</span>`
1182
- ).join('');
1183
- });
1184
-
1185
- // Hero Animations with letter-by-letter reveal (slower, more dramatic)
1186
- const heroTL = gsap.timeline({ delay: 0.5 });
1187
- heroTL
1188
- .to('.hero-badge', { opacity: 1, duration: 0.6 })
1189
- .to('#heroTitle .letter', {
1190
- opacity: 1,
1191
- y: 0,
1192
- rotateX: 0,
1193
- duration: 0.15,
1194
- stagger: 0.08,
1195
- ease: 'back.out(1.7)'
1196
- }, '-=0.2')
1197
- .to('.hero-tagline', { opacity: 1, duration: 0.6 }, '-=0.4')
1198
- .to('.hero-desc', { opacity: 1, duration: 0.6 }, '-=0.3')
1199
- .to('.hero-cta', { opacity: 1, duration: 0.5 }, '-=0.2')
1200
- .to('.scroll-indicator', { opacity: 1, duration: 0.4 });
1201
-
1202
- // Philosophy Cards
1203
- gsap.utils.toArray('.philosophy-card').forEach((card, i) => {
1204
- gsap.to(card, {
1205
- opacity: 1,
1206
- y: 0,
1207
- duration: 0.6,
1208
- delay: i * 0.15,
1209
- scrollTrigger: {
1210
- trigger: card,
1211
- start: 'top 80%',
1212
- toggleActions: 'play none none reverse'
1213
- }
1214
- });
1215
- });
1216
-
1217
- // Tool Cards
1218
- gsap.utils.toArray('.tool-card').forEach((card, i) => {
1219
- gsap.to(card, {
1220
- opacity: 1,
1221
- y: 0,
1222
- duration: 0.6,
1223
- delay: i * 0.2,
1224
- scrollTrigger: {
1225
- trigger: card,
1226
- start: 'top 80%',
1227
- toggleActions: 'play none none reverse'
1228
- }
1229
- });
1230
- });
1231
-
1232
- // Blog Cards
1233
- gsap.utils.toArray('.blog-card').forEach((card, i) => {
1234
- gsap.to(card, {
1235
- opacity: 1,
1236
- y: 0,
1237
- duration: 0.6,
1238
- delay: i * 0.15,
1239
- scrollTrigger: {
1240
- trigger: card,
1241
- start: 'top 85%',
1242
- toggleActions: 'play none none reverse'
1243
- }
1244
- });
1245
- });
1246
-
1247
- // Coming Soon Box
1248
- gsap.to('.coming-soon-box', {
1249
- opacity: 1,
1250
- duration: 0.8,
1251
- scrollTrigger: {
1252
- trigger: '.coming-soon-box',
1253
- start: 'top 80%',
1254
- toggleActions: 'play none none reverse'
1255
- }
1256
- });
1257
-
1258
- // Animated Background Pattern
1259
- const pattern = document.querySelector('.bg-pattern');
1260
- let offsetX = 0;
1261
- let offsetY = 0;
1262
-
1263
- function animatePattern() {
1264
- offsetX += 0.1;
1265
- offsetY += 0.05;
1266
- pattern.style.transform = `translate(${Math.sin(offsetX) * 5}px, ${Math.cos(offsetY) * 5}px)`;
1267
- requestAnimationFrame(animatePattern);
1268
- }
1269
- animatePattern();
1270
-
1271
- // Smooth scroll for nav links
1272
- document.querySelectorAll('a[href^="#"]').forEach(anchor => {
1273
- anchor.addEventListener('click', function (e) {
1274
- e.preventDefault();
1275
- const target = document.querySelector(this.getAttribute('href'));
1276
- if (target) {
1277
- target.scrollIntoView({ behavior: 'smooth' });
1278
- }
1279
- });
1280
- });
1281
-
1282
- // Add floating decorative elements
1283
- function createFloatingElements() {
1284
- const container = document.body;
1285
- const symbols = ['+', '*', '/', '-', '=', '>', '<', '|'];
1286
-
1287
- for (let i = 0; i < 15; i++) {
1288
- const el = document.createElement('div');
1289
- el.className = 'float-element';
1290
- el.textContent = symbols[Math.floor(Math.random() * symbols.length)];
1291
- el.style.left = Math.random() * 100 + 'vw';
1292
- el.style.top = Math.random() * 100 + 'vh';
1293
- el.style.fontSize = (Math.random() * 1.5 + 0.5) + 'rem';
1294
- el.style.padding = '10px';
1295
- container.appendChild(el);
1296
-
1297
- // Animate floating
1298
- gsap.to(el, {
1299
- y: Math.random() * 100 - 50,
1300
- x: Math.random() * 100 - 50,
1301
- rotation: Math.random() * 360,
1302
- duration: Math.random() * 10 + 10,
1303
- repeat: -1,
1304
- yoyo: true,
1305
- ease: 'sine.inOut'
1306
- });
1307
- }
1308
- }
1309
- createFloatingElements();
1310
- </script>
1311
- </body>
1312
- </html>