@hustle-together/api-dev-tools 3.2.0 → 3.5.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 (45) hide show
  1. package/README.md +712 -377
  2. package/commands/api-create.md +300 -149
  3. package/demo/hustle-together/blog/gemini-vs-claude-widgets.html +1 -1
  4. package/demo/hustle-together/blog/interview-driven-api-development.html +1 -1
  5. package/demo/hustle-together/blog/tdd-for-ai.html +1 -1
  6. package/demo/hustle-together/index.html +2 -2
  7. package/demo/workflow-demo-v3.5-backup.html +5008 -0
  8. package/demo/workflow-demo.html +5137 -3805
  9. package/hooks/enforce-deep-research.py +6 -1
  10. package/hooks/enforce-disambiguation.py +7 -1
  11. package/hooks/enforce-documentation.py +6 -1
  12. package/hooks/enforce-environment.py +5 -1
  13. package/hooks/enforce-interview.py +5 -1
  14. package/hooks/enforce-refactor.py +3 -1
  15. package/hooks/enforce-schema.py +0 -0
  16. package/hooks/enforce-scope.py +5 -1
  17. package/hooks/enforce-tdd-red.py +5 -1
  18. package/hooks/enforce-verify.py +0 -0
  19. package/hooks/track-tool-use.py +167 -0
  20. package/hooks/verify-implementation.py +0 -0
  21. package/package.json +1 -1
  22. package/templates/api-dev-state.json +24 -0
  23. package/demo/audio/audio-sync.js +0 -295
  24. package/demo/audio/generate-all-narrations.js +0 -581
  25. package/demo/audio/generate-narration.js +0 -486
  26. package/demo/audio/generate-voice-previews.js +0 -140
  27. package/demo/audio/narration-adam-timing.json +0 -4675
  28. package/demo/audio/narration-adam.mp3 +0 -0
  29. package/demo/audio/narration-creature-timing.json +0 -4675
  30. package/demo/audio/narration-creature.mp3 +0 -0
  31. package/demo/audio/narration-gaming-timing.json +0 -4675
  32. package/demo/audio/narration-gaming.mp3 +0 -0
  33. package/demo/audio/narration-hope-timing.json +0 -4675
  34. package/demo/audio/narration-hope.mp3 +0 -0
  35. package/demo/audio/narration-mark-timing.json +0 -4675
  36. package/demo/audio/narration-mark.mp3 +0 -0
  37. package/demo/audio/narration-timing.json +0 -3614
  38. package/demo/audio/narration-timing.sample.json +0 -48
  39. package/demo/audio/narration.mp3 +0 -0
  40. package/demo/audio/previews/manifest.json +0 -30
  41. package/demo/audio/previews/preview-creature.mp3 +0 -0
  42. package/demo/audio/previews/preview-gaming.mp3 +0 -0
  43. package/demo/audio/previews/preview-hope.mp3 +0 -0
  44. package/demo/audio/previews/preview-mark.mp3 +0 -0
  45. package/demo/audio/voices-manifest.json +0 -50
@@ -4,10 +4,93 @@
4
4
 
5
5
  **Purpose:** Orchestrates the complete API development workflow using interview-driven, research-first, test-first methodology with continuous verification loops.
6
6
 
7
+ ## ⚠️ CRITICAL: MANDATORY USER INTERACTION
8
+
9
+ **YOU MUST USE THE `AskUserQuestion` TOOL AT EVERY CHECKPOINT.**
10
+
11
+ This workflow requires REAL user input at each phase. You are **FORBIDDEN** from:
12
+ - Self-answering questions
13
+ - Assuming user responses
14
+ - Proceeding without explicit user confirmation
15
+ - Making decisions on behalf of the user
16
+
17
+ ### How to Ask Questions Correctly
18
+
19
+ At every `[Y/n]` or multiple-choice prompt in this workflow, you MUST call the `AskUserQuestion` tool with this EXACT schema:
20
+
21
+ ```json
22
+ {
23
+ "questions": [
24
+ {
25
+ "question": "Your question here? (must end with ?)",
26
+ "header": "Phase",
27
+ "multiSelect": false,
28
+ "options": [
29
+ {"label": "Option A", "description": "What this option means"},
30
+ {"label": "Option B", "description": "What this option means"},
31
+ {"label": "Other", "description": "I'll type my own answer"}
32
+ ]
33
+ }
34
+ ]
35
+ }
36
+ ```
37
+
38
+ **CRITICAL REQUIREMENTS:**
39
+ - `header`: Max 12 characters (e.g., "Scope", "Research", "Format")
40
+ - `options`: 2-4 options, each with `label` (1-5 words) and `description`
41
+ - `multiSelect`: Required boolean (true for checkboxes, false for radio)
42
+ - `question`: Must end with a question mark
43
+ - Users can always select "Other" to provide custom input
44
+
45
+ **WAIT for the user's response before proceeding.** The tool will show a UI dialog and pause execution until the user answers. Do NOT continue until you receive the response.
46
+
47
+ ### Violation Detection
48
+
49
+ The enforcement hooks will BLOCK your progress if:
50
+ - `user_question_asked` is false for any phase
51
+ - `user_confirmed`/`user_approved`/`user_completed` is false
52
+ - `phase_exit_confirmed` is false (user must explicitly approve proceeding to next phase)
53
+ - Questions were not asked via the AskUserQuestion tool
54
+
55
+ If you see "BLOCKED" messages, it means you skipped user interaction.
56
+
57
+ ### Phase Exit Confirmation (NEW in v3.5.0)
58
+
59
+ **Every phase requires an EXIT CONFIRMATION question** before proceeding to the next phase. This prevents Claude from self-answering and moving on without explicit user approval.
60
+
61
+ The exit confirmation question MUST:
62
+ 1. Summarize what was accomplished in the current phase
63
+ 2. Ask if user is ready to proceed to the next phase
64
+ 3. Include options like "Yes, proceed", "No, I have changes", "Add more"
65
+
66
+ Example exit confirmation:
67
+ ```json
68
+ {
69
+ "questions": [{
70
+ "question": "Phase complete. Research found 5 sources. Ready to proceed to Interview phase?",
71
+ "header": "Proceed",
72
+ "multiSelect": false,
73
+ "options": [
74
+ {"label": "Yes, proceed", "description": "Move to next phase"},
75
+ {"label": "No, more research", "description": "I need additional research on [topic]"},
76
+ {"label": "Review sources", "description": "Show me what was found"}
77
+ ]
78
+ }]
79
+ }
80
+ ```
81
+
82
+ The `phase_exit_confirmed` flag is automatically set when:
83
+ 1. An `AskUserQuestion` is called with a question containing words like "proceed", "continue", "ready", "confirm", "approve"
84
+ 2. The user responds with an affirmative answer (yes, proceed, confirm, approve, etc.)
85
+
86
+ Both conditions must be true for the flag to be set.
87
+
88
+ ---
89
+
7
90
  ## Key Principles
8
91
 
9
92
  1. **Loop Until Green** - Every verification phase loops back if not successful
10
- 2. **Continuous Interviews** - Checkpoints at EVERY phase transition
93
+ 2. **Continuous Interviews** - Checkpoints at EVERY phase transition (USE AskUserQuestion!)
11
94
  3. **Adaptive Research** - Propose searches based on context, not shotgun approach
12
95
  4. **Self-Documenting** - State file captures everything for re-grounding
13
96
  5. **Verify After Green** - Re-research docs to catch memory-based implementation errors
@@ -25,33 +108,48 @@
25
108
  │ • "[term] SDK" │
26
109
  │ • "[term] npm package" │
27
110
  │ │
28
- If multiple interpretations found, ask:
29
- ┌───────────────────────────────────────────────────────┐
30
- I found multiple things matching "[term]": │ │
31
- │ │ │ │
32
- [A] Option A - Description
33
- │ │ [B] Option B - Description │ │
34
- │ │ [C] Both │ │
35
- [D] Something else: ____
36
- └───────────────────────────────────────────────────────┘
37
-
38
- ──── Loop back if ambiguous ────
111
+ ⚠️ REQUIRED: Use AskUserQuestion tool with EXACT schema:
112
+
113
+ {
114
+ "questions": [{
115
+ "question": "Which interpretation of [term]?",
116
+ "header": "Disambig",
117
+ "multiSelect": false,
118
+ "options": [
119
+ {"label": "REST API", "description": "Official API"},│
120
+ {"label": "SDK/Package", "description": "NPM wrapper"},│
121
+ {"label": "Both", "description": "API + SDK"}
122
+ │ ] │
123
+ │ }] │
124
+ │ } │
125
+ │ │
126
+ │ WAIT for user response. Do NOT proceed without it. │
127
+ │ ──── Loop back if user selects "Something else" ──── │
39
128
  └───────────────────────────────────────────────────────────┘
40
129
  │ Clear
41
130
 
42
131
  ┌─ PHASE 1: SCOPE CONFIRMATION ─────────────────────────────┐
43
132
  │ │
44
- ┌───────────────────────────────────────────────────────┐
45
- SCOPE CONFIRMATION │ │
46
- │ │
47
- I understand you want: /api/v2/[endpoint] │ │
48
- Purpose: [inferred purpose] │ │
49
- │ External API: [service name] (URL)
50
- │ │
51
- Is this correct? [Y/n]
52
- Additional context? ____
53
- └───────────────────────────────────────────────────────┘
54
-
133
+ Present your understanding, then:
134
+
135
+ ⚠️ REQUIRED: Use AskUserQuestion tool:
136
+
137
+ AskUserQuestion({
138
+ questions: [{
139
+ question: "I understand you want /api/v2/[endpoint]
140
+ to [purpose]. Using [external API].
141
+ Is this correct?",
142
+ header: "Scope",
143
+ options: [
144
+ │ "Yes, proceed", │
145
+ │ "I have modifications to add", │
146
+ │ "No, let me clarify the purpose" │
147
+ │ ] │
148
+ │ }] │
149
+ │ }) │
150
+ │ │
151
+ │ WAIT for user response. Do NOT assume "yes". │
152
+ │ ──── Loop back if user has modifications ──── │
55
153
  └───────────────────────────────────────────────────────────┘
56
154
 
57
155
 
@@ -62,132 +160,178 @@
62
160
  │ • WebSearch: "[name] official documentation" │
63
161
  │ • WebSearch: "site:[domain] api reference" │
64
162
  │ │
65
- │ Present summary:
66
- ┌───────────────────────────────────────────────────────┐
67
- RESEARCH SUMMARY │
68
- │ │
69
- │ Source │ Found │ │
70
- │ │ ├────────────────┼────────────────────────────────────│ │
71
- Official docs │ [URL]
72
- API Reference │ ✓ REST v2
73
- │ │ │ Auth method │ ✓ Bearer token │ │
74
- │ │ │ NPM package │ ? Not found │ │
75
- │ │ │ │
76
- Proceed? [Y] / Search more? [n] ____
77
- └───────────────────────────────────────────────────────┘
78
-
163
+ │ Present summary table, then:
164
+
165
+ ⚠️ REQUIRED: Use AskUserQuestion tool:
166
+
167
+ AskUserQuestion({
168
+ questions: [{
169
+ question: "Research summary above. Found [N]
170
+ sources. Should I proceed or search
171
+ for more?",
172
+ header: "Research",
173
+ options: [
174
+ "Proceed to interview",
175
+ "Search more - I need [specific topic]",
176
+ "Search for something specific (I'll describe)"
177
+ │ ] │
178
+ │ }] │
179
+ │ }) │
180
+ │ │
181
+ │ WAIT for user response. Do NOT auto-proceed. │
79
182
  │ ──── Loop back if user wants more research ──── │
80
183
  └───────────────────────────────────────────────────────────┘
81
184
 
82
185
 
83
186
  ┌─ PHASE 3: INTERVIEW (Generated FROM Research) ────────────┐
84
187
  │ │
85
- Generate questions based on discovered parameters:
86
-
87
- ┌───────────────────────────────────────────────────────┐
88
- Based on research, [API] has N parameters:
89
- │ │
90
- 1. DOMAIN (required) - string
91
- │ → No question needed (always required)
92
- │ │ │ │
93
- 2. FORMAT: ["json", "svg", "png", "raw"]
94
- │ Q: Which formats do you need?
95
- │ │ [x] json [x] svg [x] png [ ] raw │ │
96
- │ │
97
- 3. QUALITY: 1-100 (continuous range) │ │
98
- Q: How should we TEST this? │ │
99
- │ [ ] All values (100 tests) │
100
- [x] Boundary (1, 50, 100) │ │
101
- │ [ ] Sample (1, 25, 50, 75, 100)
102
- [ ] Custom: ____ │ │
103
- │ │ │ │
104
- ... (continues for ALL discovered parameters)
105
- └───────────────────────────────────────────────────────┘
106
-
107
- Decisions are saved to state file for consistency.
188
+ For EACH parameter discovered in research, ask ONE
189
+ question at a time using AskUserQuestion:
190
+
191
+ ⚠️ REQUIRED: Use AskUserQuestion for EACH question:
192
+
193
+ // Question 1: Format preference
194
+ AskUserQuestion({
195
+ questions: [{
196
+ question: "Which response formats do you need?",
197
+ header: "Format",
198
+ multiSelect: true,
199
+ options: ["JSON", "SVG", "PNG", "All formats"]
200
+ }]
201
+ })
202
+ // WAIT for response, record in state, then next Q
203
+
204
+ // Question 2: Error handling
205
+ AskUserQuestion({
206
+ questions: [{
207
+ question: "How should errors be handled?",
208
+ header: "Errors",
209
+ options: [
210
+ "Throw exceptions",
211
+ │ "Return error objects", │
212
+ │ "Both (configurable)" │
213
+ │ ] │
214
+ │ }] │
215
+ │ }) │
216
+ │ // Continue for ALL parameters... │
217
+ │ │
218
+ │ After ALL questions answered: │
219
+ │ │
220
+ │ AskUserQuestion({ │
221
+ │ questions: [{ │
222
+ │ question: "Interview complete. Your decisions: │
223
+ │ [summary]. All correct?", │
224
+ │ header: "Confirm", │
225
+ │ options: [ │
226
+ │ "Yes, proceed to schema", │
227
+ │ "Change an answer", │
228
+ │ "Add another question" │
229
+ │ ] │
230
+ │ }] │
231
+ │ }) │
232
+ │ │
233
+ │ Decisions saved to state file for consistency. │
234
+ │ ──── Loop back if user wants to change answers ──── │
108
235
  └───────────────────────────────────────────────────────────┘
109
236
 
110
237
 
111
238
  ┌─ PHASE 4: DEEP RESEARCH (Adaptive) ───────────────────────┐
112
239
  │ │
113
- │ Based on interview answers, PROPOSE additional research:
114
- │ │
115
- │ ┌───────────────────────────────────────────────────────┐ │
116
- │ │ PROPOSED DEEP RESEARCH │ │
117
- │ │ │ │
118
- │ │ Based on your selections, I want to research: │ │
119
- │ │ │ │
120
- │ │ [x] Error response format (for error handling) │ │
121
- │ │ [x] Rate limiting behavior (short cache selected) │ │
122
- │ │ [ ] Webhook support (not selected) │ │
123
- │ │ [x] SVG optimization (SVG format selected) │ │
124
- │ │ │ │
125
- │ │ Approve these searches? [Y] │ │
126
- │ │ Add more: ____ │ │
127
- │ │ Skip and proceed: [n] │ │
128
- │ └───────────────────────────────────────────────────────┘ │
240
+ │ Based on interview answers, PROPOSE additional research.
129
241
  │ │
242
+ │ ⚠️ REQUIRED: Use AskUserQuestion tool: │
243
+ │ │
244
+ │ AskUserQuestion({ │
245
+ │ questions: [{ │
246
+ │ question: "Based on your interview answers, I │
247
+ │ want to research: [list]. Approve?", │
248
+ │ header: "Deep Research", │
249
+ │ options: [ │
250
+ │ "Yes, run these searches", │
251
+ │ "Add more - I also need [topic]", │
252
+ │ "Skip deep research, proceed to schema" │
253
+ │ ] │
254
+ │ }] │
255
+ │ }) │
256
+ │ │
257
+ │ WAIT for user response. Do NOT auto-approve. │
130
258
  │ KEY: Research is PROPOSED, not automatic shotgun. │
259
+ │ ──── Loop back if user wants to add topics ──── │
131
260
  └───────────────────────────────────────────────────────────┘
132
261
 
133
262
 
134
263
  ┌─ PHASE 5: SCHEMA DESIGN ──────────────────────────────────┐
135
264
  │ │
136
- │ Create Zod schema from research + interview:
137
- │ │
138
- ┌───────────────────────────────────────────────────────┐
139
- SCHEMA REVIEW │ │
140
- │ │
141
- │ const RequestSchema = z.object({
142
- │ domain: z.string().min(1),
143
- │ format: z.enum(["json", "svg", "png"]), │
144
- quality: z.number().min(1).max(100).default(80) │ │
145
- │ │ }); │ │
146
- │ │ │ │
147
- Schema matches interview answers? [Y/n]
148
- Missing fields? ____ │
149
- └───────────────────────────────────────────────────────┘
150
-
151
- ──── Loop back if schema wrong ────
265
+ │ Create Zod schema from research + interview, then:
266
+ │ │
267
+ ⚠️ REQUIRED: Use AskUserQuestion tool:
268
+
269
+ AskUserQuestion({
270
+ questions: [{
271
+ question: "Schema created based on your interview.
272
+ [show schema]. Does this match your
273
+ requirements?",
274
+ header: "Schema",
275
+ options: [
276
+ "Yes, schema looks correct",
277
+ "No, I need changes (I'll describe)",
278
+ "Let's redo the interview"
279
+ ]
280
+ }]
281
+ │ }) │
282
+ │ │
283
+ │ WAIT for user response. Do NOT assume approval. │
284
+ │ ──── Loop back if schema needs changes ──── │
152
285
  └───────────────────────────────────────────────────────────┘
153
286
 
154
287
 
155
288
  ┌─ PHASE 6: ENVIRONMENT CHECK ──────────────────────────────┐
156
289
  │ │
157
- ┌───────────────────────────────────────────────────────┐
158
- ENVIRONMENT CHECK │ │
159
- │ │
160
- │ │ │ Variable │ Status │ Source │ │
161
- │ │ ├──────────────────┼──────────┼──────────────────────│ │
162
- │ │ │ API_KEY │ ✓ Found │ .env.local │ │
163
- │ │ │ │
164
- │ │ Ready for testing? [Y/n] │ │
165
- │ └───────────────────────────────────────────────────────┘ │
290
+ Check required API keys, show status table, then:
291
+
292
+ ⚠️ REQUIRED: Use AskUserQuestion tool:
166
293
  │ │
294
+ │ AskUserQuestion({ │
295
+ │ questions: [{ │
296
+ │ question: "Environment check: [N] keys found, │
297
+ │ [M] missing. Ready to start TDD?", │
298
+ │ header: "Environment", │
299
+ │ options: [ │
300
+ │ "Yes, ready to write tests", │
301
+ │ "No, need to set up API keys first", │
302
+ │ "No, need to fix something else" │
303
+ │ ] │
304
+ │ }] │
305
+ │ }) │
306
+ │ │
307
+ │ WAIT for user response. Do NOT auto-proceed. │
167
308
  │ ──── Loop back if keys missing ──── │
168
309
  └───────────────────────────────────────────────────────────┘
169
310
 
170
311
 
171
312
  ┌─ PHASE 7: TDD RED (Write Failing Tests) ──────────────────┐
172
313
  │ │
173
- │ Generate test matrix from schema + interview decisions:
174
- │ │
175
- ┌───────────────────────────────────────────────────────┐
176
- TEST MATRIX REVIEW │ │
177
- │ │
178
- │ │ │ Parameter │ Valid Values │ Invalid Values │ │
179
- ├────────────┼────────────────────┼───────────────────│
180
- domain │ "x.com", "a.co" "", null │ │
181
- format │ "json", "svg" "gif", "webp" │ │
182
- │ │ quality │ 1, 50, 100 0, 101, -1 │ │
183
- │ │ │ │
184
- Total tests: 23 (pairwise reduction from 156)
185
- │ │
186
- Approve test matrix? [Y/n]
187
- Add test cases? ____ │ │
188
- └───────────────────────────────────────────────────────┘
189
-
314
+ │ Generate test matrix from schema + interview, then:
315
+ │ │
316
+ ⚠️ REQUIRED: Use AskUserQuestion tool:
317
+
318
+ AskUserQuestion({
319
+ questions: [{
320
+ question: "Test matrix: [N] test scenarios based
321
+ on your interview. Covers: [list].
322
+ Approve this test plan?",
323
+ header: "Tests",
324
+ options: [
325
+ "Yes, write these tests",
326
+ "Add more scenarios (I'll describe)",
327
+ "Change a scenario (I'll describe)"
328
+ ]
329
+ }]
330
+ })
331
+ │ │
332
+ │ WAIT for user response. Do NOT auto-approve. │
190
333
  │ HOOK: PreToolUse blocks Write if no research/interview │
334
+ │ ──── Loop back if user wants changes ──── │
191
335
  └───────────────────────────────────────────────────────────┘
192
336
 
193
337
 
@@ -206,27 +350,27 @@
206
350
  ┌─ PHASE 9: VERIFY (Re-Research After Green) ───────────────┐
207
351
  │ │
208
352
  │ MANDATORY: Re-read original documentation. │
209
- │ Compare implementation to docs feature-by-feature:
210
- │ │
211
- ┌───────────────────────────────────────────────────────┐
212
- IMPLEMENTATION VERIFICATION │ │
213
- │ │
214
- │ │ │ Feature │ In Docs │ Implemented │ Status │ │
215
- ├───────────────┼─────────┼─────────────┼─────────────│
216
- domain param ✓ │ ✓ │ ✅ Match │ │
217
- format opts 4 │ 3 │ ⚠️ Missing │ │
218
- │ │ │ size max │ 4096 │ 2048 │ ⚠️ Wrong │ │
219
- │ │ webhook ✗ │ ℹ️ Optional │ │
220
- │ │
221
- DISCREPANCIES: 2 found │
222
- │ │
223
- │ Fix these gaps? [Y] → Returns to Phase 7 (Red)
224
- │ Skip (intentional)? [n] → Document as omissions
225
- └───────────────────────────────────────────────────────┘
226
- │ │
353
+ │ Compare implementation to docs, then:
354
+ │ │
355
+ ⚠️ REQUIRED: Use AskUserQuestion tool:
356
+
357
+ AskUserQuestion({
358
+ questions: [{
359
+ question: "Verification found [N] gap(s) between
360
+ docs and implementation: [list].
361
+ How should I proceed?",
362
+ header: "Verify",
363
+ options: [
364
+ "Fix gaps - loop back to Red phase",
365
+ "Skip - these are intentional omissions",
366
+ "Fix some, skip others (I'll specify)"
367
+ ]
368
+ }]
369
+ })
370
+ │ │
371
+ │ WAIT for user response. Do NOT auto-decide. │
227
372
  │ HOOK: PostToolUse triggers after test pass │
228
-
229
- │ ──── Loop back to Phase 7 if gaps need fixing ──── │
373
+ ──── Loop back to Phase 7 if user wants fixes ────
230
374
  └───────────────────────────────────────────────────────────┘
231
375
 
232
376
 
@@ -244,19 +388,26 @@
244
388
 
245
389
  ┌─ PHASE 11: DOCUMENTATION ─────────────────────────────────┐
246
390
  │ │
247
- ┌───────────────────────────────────────────────────────┐
248
- DOCUMENTATION REVIEW │ │
249
- │ │
250
- Files to update: │ │
251
- │ │
252
- [x] api-tests-manifest.json - Manifest entry
253
- [x] OpenAPI spec - Endpoint documented
254
- [x] .claude/research/[api]/CURRENT.md - Cached
255
- │ │ │ │
256
- │ Documentation complete? [Y/n]
257
- └───────────────────────────────────────────────────────┘
391
+ Update documentation files, then:
392
+
393
+ ⚠️ REQUIRED: Use AskUserQuestion tool:
394
+
395
+ AskUserQuestion({
396
+ questions: [{
397
+ question: "Documentation checklist: [list files].
398
+ All documentation complete?",
399
+ header: "Docs",
400
+ options: [
401
+ "Yes, all documentation is done",
402
+ │ "No, I need to add something (I'll describe)", │
403
+ │ "Skip docs for now (not recommended)" │
404
+ │ ] │
405
+ │ }] │
406
+ │ }) │
258
407
  │ │
408
+ │ WAIT for user response. Do NOT auto-complete. │
259
409
  │ HOOK: Stop hook blocks if docs incomplete │
410
+ │ ──── Loop back if user needs to add docs ──── │
260
411
  └───────────────────────────────────────────────────────────┘
261
412
 
262
413
 
@@ -7,7 +7,7 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
9
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Fredoka:wght@600;700&display=swap" rel="stylesheet">
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.14.1/gsap.min.js"></script>
11
11
  <style>
12
12
  * { margin: 0; padding: 0; box-sizing: border-box; }
13
13
 
@@ -7,7 +7,7 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
9
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Fredoka:wght@600;700&display=swap" rel="stylesheet">
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.14.1/gsap.min.js"></script>
11
11
  <style>
12
12
  * {
13
13
  margin: 0;
@@ -7,7 +7,7 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
9
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&family=Fredoka:wght@600;700&display=swap" rel="stylesheet">
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.14.1/gsap.min.js"></script>
11
11
  <style>
12
12
  * { margin: 0; padding: 0; box-sizing: border-box; }
13
13
 
@@ -7,8 +7,8 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
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.12.2/gsap.min.js"></script>
11
- <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
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
12
  <style>
13
13
  /* ============================================
14
14
  HUSTLE TOGETHER - 90s BOXY AESTHETIC