@kolbo/kolbo-code-linux-arm64-musl 1.1.73 → 2.0.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.
- package/bin/kolbo +0 -0
- package/package.json +1 -1
- package/skills/brainstorming/SKILL.md +164 -0
- package/skills/brainstorming/scripts/frame-template.html +214 -0
- package/skills/brainstorming/scripts/helper.js +88 -0
- package/skills/brainstorming/scripts/server.cjs +354 -0
- package/skills/brainstorming/scripts/start-server.sh +148 -0
- package/skills/brainstorming/scripts/stop-server.sh +56 -0
- package/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/skills/brainstorming/visual-companion.md +287 -0
- package/skills/dispatching-parallel-agents/SKILL.md +182 -0
- package/skills/docx/.skillfish.json +10 -0
- package/skills/docx/SKILL.md +196 -0
- package/skills/docx/docx-js.md +350 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/docx/ooxml/scripts/pack.py +159 -0
- package/skills/docx/ooxml/scripts/unpack.py +29 -0
- package/skills/docx/ooxml/scripts/validate.py +69 -0
- package/skills/docx/ooxml/scripts/validation/__init__.py +15 -0
- package/skills/docx/ooxml/scripts/validation/base.py +951 -0
- package/skills/docx/ooxml/scripts/validation/docx.py +274 -0
- package/skills/docx/ooxml/scripts/validation/pptx.py +315 -0
- package/skills/docx/ooxml/scripts/validation/redlining.py +279 -0
- package/skills/docx/ooxml.md +599 -0
- package/skills/docx/scripts/__init__.py +1 -0
- package/skills/docx/scripts/document.py +1272 -0
- package/skills/docx/scripts/templates/comments.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/skills/docx/scripts/templates/people.xml +3 -0
- package/skills/docx/scripts/utilities.py +374 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +200 -0
- package/skills/fullstack-app/SKILL.md +621 -0
- package/skills/kolbo/SKILL.md +19 -263
- package/skills/ollama-vision/SKILL.md +105 -0
- package/skills/pdf/.skillfish.json +10 -0
- package/skills/pdf/FORMS.md +205 -0
- package/skills/pdf/REFERENCE.md +612 -0
- package/skills/pdf/SKILL.md +293 -0
- package/skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/skills/pdf/scripts/create_validation_image.py +41 -0
- package/skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/skills/pdf/scripts/fill_fillable_fields.py +114 -0
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/skills/photo-studio/SKILL.md +122 -0
- package/skills/pptx/.skillfish.json +10 -0
- package/skills/pptx/SKILL.md +483 -0
- package/skills/pptx/html2pptx.md +626 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/pptx/ooxml/scripts/pack.py +159 -0
- package/skills/pptx/ooxml/scripts/unpack.py +29 -0
- package/skills/pptx/ooxml/scripts/validate.py +69 -0
- package/skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/skills/pptx/ooxml/scripts/validation/base.py +951 -0
- package/skills/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/skills/pptx/ooxml.md +427 -0
- package/skills/pptx/scripts/html2pptx.js +995 -0
- package/skills/pptx/scripts/inventory.py +1020 -0
- package/skills/pptx/scripts/rearrange.py +231 -0
- package/skills/pptx/scripts/replace.py +385 -0
- package/skills/pptx/scripts/thumbnail.py +450 -0
- package/skills/receiving-code-review/SKILL.md +213 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +146 -0
- package/skills/subagent-driven-development/SKILL.md +277 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +113 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/supabase/.skillfish.json +10 -0
- package/skills/supabase/SKILL.md +106 -0
- package/skills/supabase/assets/feedback-issue-template.md +17 -0
- package/skills/supabase/references/skill-feedback.md +17 -0
- package/skills/supabase-postgres-best-practices/.skillfish.json +10 -0
- package/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/skills/supabase-postgres-best-practices/references/_contributing.md +170 -0
- package/skills/supabase-postgres-best-practices/references/_sections.md +39 -0
- package/skills/supabase-postgres-best-practices/references/_template.md +34 -0
- package/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/skills/supabase-postgres-best-practices/references/security-rls-performance.md +57 -0
- package/skills/supabase-quickstart/SKILL.md +400 -0
- package/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/skills/systematic-debugging/SKILL.md +296 -0
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/skills/systematic-debugging/find-polluter.sh +63 -0
- package/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/skills/systematic-debugging/test-academic.md +14 -0
- package/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/skills/test-driven-development/SKILL.md +371 -0
- package/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/skills/using-git-worktrees/SKILL.md +218 -0
- package/skills/using-superpowers/SKILL.md +115 -0
- package/skills/using-superpowers/references/codex-tools.md +100 -0
- package/skills/using-superpowers/references/gemini-tools.md +33 -0
- package/skills/verification-before-completion/SKILL.md +139 -0
- package/skills/video-production/SKILL.md +8 -7
- package/skills/writing-plans/SKILL.md +152 -0
- package/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/skills/writing-skills/SKILL.md +655 -0
- package/skills/writing-skills/anthropic-best-practices.md +1150 -0
- package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/skills/writing-skills/persuasion-principles.md +187 -0
- package/skills/writing-skills/render-graphs.js +168 -0
- package/skills/writing-skills/testing-skills-with-subagents.md +384 -0
- package/skills/xlsx/.skillfish.json +10 -0
- package/skills/xlsx/SKILL.md +288 -0
- package/skills/xlsx/recalc.py +178 -0
- package/skills/color-grading/SKILL.md +0 -152
- package/skills/ffmpeg-patterns/SKILL.md +0 -240
- package/skills/image-prompting-guide/SKILL.md +0 -143
- package/skills/music-prompting/SKILL.md +0 -146
- package/skills/production-review/SKILL.md +0 -152
- package/skills/short-form-video/SKILL.md +0 -168
- package/skills/sound-design/SKILL.md +0 -154
- package/skills/storytelling/SKILL.md +0 -139
- package/skills/subtitle-production/SKILL.md +0 -244
- package/skills/subtitle-production/reference/burn_to_video.py +0 -222
- package/skills/subtitle-production/reference/export_srts.py +0 -127
- package/skills/subtitle-production/reference/gen_srt.py +0 -42
- package/skills/typography-video/SKILL.md +0 -182
- package/skills/typography-video/reference/KineticTitleScene.tsx +0 -345
- package/skills/video-editing/SKILL.md +0 -128
- package/skills/video-prompting-guide/SKILL.md +0 -268
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fullstack-app
|
|
3
|
+
description: "Use when a user wants to build a complete web application from scratch. Triggers: 'build me an app', 'create a SaaS', 'I need a web app', 'make me a dashboard', 'create a todo app', or any request to scaffold a fullstack project. Orchestrates the opinionated stack (Next.js 15 + Tailwind CSS + shadcn/ui + Supabase), page templates, data patterns, and generates AGENTS.md."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fullstack App Builder
|
|
7
|
+
|
|
8
|
+
You are building a production-quality fullstack web application. Follow this skill exactly — do NOT offer alternatives, do NOT ask the user to choose between ORMs/state managers/CSS frameworks. There is ONE stack and ONE way to do things.
|
|
9
|
+
|
|
10
|
+
## The Stack (non-negotiable)
|
|
11
|
+
|
|
12
|
+
| Layer | Choice |
|
|
13
|
+
|-------|--------|
|
|
14
|
+
| Framework | Next.js 15 (App Router) |
|
|
15
|
+
| Language | TypeScript (strict) |
|
|
16
|
+
| Styling | Tailwind CSS |
|
|
17
|
+
| Components | shadcn/ui (Radix + Tailwind) |
|
|
18
|
+
| Backend | Supabase (Postgres + Auth + Storage + Realtime) |
|
|
19
|
+
| Data fetching | TanStack Query (React Query) |
|
|
20
|
+
| Forms | react-hook-form + zod |
|
|
21
|
+
| Auth | @supabase/ssr |
|
|
22
|
+
|
|
23
|
+
If the user explicitly requests React+Vite instead of Next.js, see the Vite Variant section at the bottom. Otherwise, default to Next.js without asking.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Phase 0: Understand the App
|
|
28
|
+
|
|
29
|
+
Ask the user (skip any already answered):
|
|
30
|
+
1. **What does the app do?** (e.g., "todo app", "SaaS dashboard", "booking system")
|
|
31
|
+
2. **What are the main entities?** (e.g., "tasks, projects, users" — if unclear, suggest based on #1)
|
|
32
|
+
3. **Do they already have a Supabase project?** (yes → get URL + anon key; no → will guide setup)
|
|
33
|
+
|
|
34
|
+
Do NOT ask about framework, styling, or component library — the stack is decided.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Phase 1: Scaffold the Project
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx create-next-app@latest <app-name> --ts --tailwind --app --eslint --src-dir --import-alias "@/*"
|
|
42
|
+
cd <app-name>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Initialize shadcn/ui
|
|
46
|
+
```bash
|
|
47
|
+
npx shadcn@latest init
|
|
48
|
+
```
|
|
49
|
+
When prompted: New York style, Zinc base color, CSS variables YES.
|
|
50
|
+
|
|
51
|
+
### Install base components (always needed)
|
|
52
|
+
```bash
|
|
53
|
+
npx shadcn@latest add button card input label form separator
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Install core packages
|
|
57
|
+
```bash
|
|
58
|
+
npm install @supabase/supabase-js @supabase/ssr @tanstack/react-query react-hook-form @hookform/resolvers zod lucide-react
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Create directory structure
|
|
62
|
+
```
|
|
63
|
+
src/
|
|
64
|
+
├── app/
|
|
65
|
+
│ ├── (auth)/ # Auth pages (login, signup, etc.)
|
|
66
|
+
│ │ ├── layout.tsx
|
|
67
|
+
│ │ ├── login/page.tsx
|
|
68
|
+
│ │ ├── signup/page.tsx
|
|
69
|
+
│ │ ├── forgot-password/page.tsx
|
|
70
|
+
│ │ └── callback/route.ts
|
|
71
|
+
│ ├── (dashboard)/ # Protected app pages
|
|
72
|
+
│ │ ├── layout.tsx
|
|
73
|
+
│ │ ├── page.tsx # Dashboard home
|
|
74
|
+
│ │ ├── settings/page.tsx
|
|
75
|
+
│ │ └── <entity>/ # One per entity
|
|
76
|
+
│ │ ├── page.tsx # List view
|
|
77
|
+
│ │ └── [id]/page.tsx
|
|
78
|
+
│ ├── (marketing)/ # Public pages
|
|
79
|
+
│ │ ├── layout.tsx
|
|
80
|
+
│ │ └── page.tsx # Landing page
|
|
81
|
+
│ ├── layout.tsx # Root layout
|
|
82
|
+
│ └── providers.tsx # TanStack Query provider
|
|
83
|
+
├── components/
|
|
84
|
+
│ ├── ui/ # shadcn/ui components (auto-generated)
|
|
85
|
+
│ └── <shared components>
|
|
86
|
+
├── hooks/
|
|
87
|
+
│ └── use-<entity>.ts # TanStack Query hooks per entity
|
|
88
|
+
├── lib/
|
|
89
|
+
│ └── supabase/
|
|
90
|
+
│ ├── client.ts # Browser client
|
|
91
|
+
│ └── server.ts # Server client
|
|
92
|
+
├── middleware.ts # Auth middleware
|
|
93
|
+
└── types/
|
|
94
|
+
└── database.ts # Supabase types
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Run and verify
|
|
98
|
+
```bash
|
|
99
|
+
npm run dev
|
|
100
|
+
```
|
|
101
|
+
Fix ANY errors before proceeding.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Phase 2: Backend Setup
|
|
106
|
+
|
|
107
|
+
**Load the `supabase-quickstart` skill** and execute its Phases 1-2:
|
|
108
|
+
- Phase 1: Supabase project setup (or connect existing)
|
|
109
|
+
- Phase 2: CLI install, MCP server config, project linking
|
|
110
|
+
|
|
111
|
+
**Skip** supabase-quickstart's Phase 3 (frontend scaffolding) — we already have the frontend.
|
|
112
|
+
|
|
113
|
+
After Supabase is connected, create the client files:
|
|
114
|
+
|
|
115
|
+
### `src/lib/supabase/client.ts`
|
|
116
|
+
```typescript
|
|
117
|
+
import { createBrowserClient } from "@supabase/ssr"
|
|
118
|
+
|
|
119
|
+
export function createClient() {
|
|
120
|
+
return createBrowserClient(
|
|
121
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
122
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `src/lib/supabase/server.ts`
|
|
128
|
+
```typescript
|
|
129
|
+
import { createServerClient } from "@supabase/ssr"
|
|
130
|
+
import { cookies } from "next/headers"
|
|
131
|
+
|
|
132
|
+
export async function createClient() {
|
|
133
|
+
const cookieStore = await cookies()
|
|
134
|
+
return createServerClient(
|
|
135
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
136
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
137
|
+
{
|
|
138
|
+
cookies: {
|
|
139
|
+
getAll() { return cookieStore.getAll() },
|
|
140
|
+
setAll(cookiesToSet) {
|
|
141
|
+
try {
|
|
142
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
143
|
+
cookieStore.set(name, value, options))
|
|
144
|
+
} catch {}
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### `src/middleware.ts`
|
|
153
|
+
```typescript
|
|
154
|
+
import { createServerClient } from "@supabase/ssr"
|
|
155
|
+
import { NextResponse, type NextRequest } from "next/server"
|
|
156
|
+
|
|
157
|
+
export async function middleware(request: NextRequest) {
|
|
158
|
+
let response = NextResponse.next({ request })
|
|
159
|
+
const supabase = createServerClient(
|
|
160
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
161
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
162
|
+
{
|
|
163
|
+
cookies: {
|
|
164
|
+
getAll() { return request.cookies.getAll() },
|
|
165
|
+
setAll(cookiesToSet) {
|
|
166
|
+
cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value))
|
|
167
|
+
response = NextResponse.next({ request })
|
|
168
|
+
cookiesToSet.forEach(({ name, value, options }) =>
|
|
169
|
+
response.cookies.set(name, value, options))
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
)
|
|
174
|
+
const { data: { user } } = await supabase.auth.getUser()
|
|
175
|
+
|
|
176
|
+
if (!user && request.nextUrl.pathname.startsWith("/(dashboard)")) {
|
|
177
|
+
return NextResponse.redirect(new URL("/login", request.url))
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (user && request.nextUrl.pathname.startsWith("/(auth)")) {
|
|
181
|
+
return NextResponse.redirect(new URL("/", request.url))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return response
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export const config = {
|
|
188
|
+
matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)"],
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `.env.local`
|
|
193
|
+
```
|
|
194
|
+
NEXT_PUBLIC_SUPABASE_URL=<from Supabase dashboard>
|
|
195
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=<from Supabase dashboard>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Run `npm run dev` — fix any errors before proceeding.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Phase 3: Page Templates
|
|
203
|
+
|
|
204
|
+
### Auth Pages
|
|
205
|
+
|
|
206
|
+
Install components:
|
|
207
|
+
```bash
|
|
208
|
+
npx shadcn@latest add card form input label button separator
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### `src/app/(auth)/layout.tsx`
|
|
212
|
+
```tsx
|
|
213
|
+
export default function AuthLayout({ children }: { children: React.ReactNode }) {
|
|
214
|
+
return (
|
|
215
|
+
<div className="flex min-h-screen items-center justify-center bg-muted/50 p-4">
|
|
216
|
+
<div className="w-full max-w-md">{children}</div>
|
|
217
|
+
</div>
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### `src/app/(auth)/login/page.tsx`
|
|
223
|
+
```tsx
|
|
224
|
+
"use client"
|
|
225
|
+
|
|
226
|
+
import { useRouter } from "next/navigation"
|
|
227
|
+
import { useForm } from "react-hook-form"
|
|
228
|
+
import { zodResolver } from "@hookform/resolvers/zod"
|
|
229
|
+
import { z } from "zod"
|
|
230
|
+
import Link from "next/link"
|
|
231
|
+
import { createClient } from "@/lib/supabase/client"
|
|
232
|
+
import { Button } from "@/components/ui/button"
|
|
233
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
234
|
+
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
|
|
235
|
+
import { Input } from "@/components/ui/input"
|
|
236
|
+
|
|
237
|
+
const schema = z.object({
|
|
238
|
+
email: z.string().email(),
|
|
239
|
+
password: z.string().min(6, "Password must be at least 6 characters"),
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
export default function LoginPage() {
|
|
243
|
+
const router = useRouter()
|
|
244
|
+
const supabase = createClient()
|
|
245
|
+
const form = useForm<z.infer<typeof schema>>({
|
|
246
|
+
resolver: zodResolver(schema),
|
|
247
|
+
defaultValues: { email: "", password: "" },
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
async function onSubmit(values: z.infer<typeof schema>) {
|
|
251
|
+
const { error } = await supabase.auth.signInWithPassword(values)
|
|
252
|
+
if (error) {
|
|
253
|
+
form.setError("root", { message: error.message })
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
router.push("/")
|
|
257
|
+
router.refresh()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<Card>
|
|
262
|
+
<CardHeader>
|
|
263
|
+
<CardTitle>Log in</CardTitle>
|
|
264
|
+
<CardDescription>Enter your credentials to access your account</CardDescription>
|
|
265
|
+
</CardHeader>
|
|
266
|
+
<CardContent>
|
|
267
|
+
<Form {...form}>
|
|
268
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
|
269
|
+
<FormField control={form.control} name="email" render={({ field }) => (
|
|
270
|
+
<FormItem>
|
|
271
|
+
<FormLabel>Email</FormLabel>
|
|
272
|
+
<FormControl><Input type="email" placeholder="you@example.com" {...field} /></FormControl>
|
|
273
|
+
<FormMessage />
|
|
274
|
+
</FormItem>
|
|
275
|
+
)} />
|
|
276
|
+
<FormField control={form.control} name="password" render={({ field }) => (
|
|
277
|
+
<FormItem>
|
|
278
|
+
<FormLabel>Password</FormLabel>
|
|
279
|
+
<FormControl><Input type="password" {...field} /></FormControl>
|
|
280
|
+
<FormMessage />
|
|
281
|
+
</FormItem>
|
|
282
|
+
)} />
|
|
283
|
+
{form.formState.errors.root && (
|
|
284
|
+
<p className="text-sm text-destructive">{form.formState.errors.root.message}</p>
|
|
285
|
+
)}
|
|
286
|
+
<Button type="submit" className="w-full" disabled={form.formState.isSubmitting}>
|
|
287
|
+
{form.formState.isSubmitting ? "Signing in..." : "Sign in"}
|
|
288
|
+
</Button>
|
|
289
|
+
</form>
|
|
290
|
+
</Form>
|
|
291
|
+
<div className="mt-4 text-center text-sm text-muted-foreground">
|
|
292
|
+
Don't have an account? <Link href="/signup" className="underline">Sign up</Link>
|
|
293
|
+
</div>
|
|
294
|
+
<div className="mt-2 text-center text-sm">
|
|
295
|
+
<Link href="/forgot-password" className="text-muted-foreground underline">Forgot password?</Link>
|
|
296
|
+
</div>
|
|
297
|
+
</CardContent>
|
|
298
|
+
</Card>
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Create similar pages for `/signup` (uses `supabase.auth.signUp`) and `/forgot-password` (uses `supabase.auth.resetPasswordForEmail`). Follow the same pattern: Card + Form + zod schema.
|
|
304
|
+
|
|
305
|
+
#### `src/app/(auth)/callback/route.ts`
|
|
306
|
+
```typescript
|
|
307
|
+
import { createClient } from "@/lib/supabase/server"
|
|
308
|
+
import { NextResponse } from "next/server"
|
|
309
|
+
|
|
310
|
+
export async function GET(request: Request) {
|
|
311
|
+
const { searchParams, origin } = new URL(request.url)
|
|
312
|
+
const code = searchParams.get("code")
|
|
313
|
+
const next = searchParams.get("next") ?? "/"
|
|
314
|
+
|
|
315
|
+
if (code) {
|
|
316
|
+
const supabase = await createClient()
|
|
317
|
+
const { error } = await supabase.auth.exchangeCodeForSession(code)
|
|
318
|
+
if (!error) return NextResponse.redirect(`${origin}${next}`)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return NextResponse.redirect(`${origin}/login?error=auth`)
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Dashboard Layout
|
|
326
|
+
|
|
327
|
+
Install components:
|
|
328
|
+
```bash
|
|
329
|
+
npx shadcn@latest add sidebar navigation-menu avatar dropdown-menu sheet breadcrumb
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### `src/app/(dashboard)/layout.tsx`
|
|
333
|
+
```tsx
|
|
334
|
+
import { createClient } from "@/lib/supabase/server"
|
|
335
|
+
import { redirect } from "next/navigation"
|
|
336
|
+
import { AppSidebar } from "@/components/app-sidebar"
|
|
337
|
+
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
|
|
338
|
+
|
|
339
|
+
export default async function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|
340
|
+
const supabase = await createClient()
|
|
341
|
+
const { data: { user } } = await supabase.auth.getUser()
|
|
342
|
+
if (!user) redirect("/login")
|
|
343
|
+
|
|
344
|
+
return (
|
|
345
|
+
<SidebarProvider>
|
|
346
|
+
<div className="flex min-h-screen w-full">
|
|
347
|
+
<AppSidebar user={user} />
|
|
348
|
+
<main className="flex-1 overflow-auto">
|
|
349
|
+
<div className="flex items-center gap-2 border-b px-4 py-3">
|
|
350
|
+
<SidebarTrigger />
|
|
351
|
+
</div>
|
|
352
|
+
<div className="p-6">{children}</div>
|
|
353
|
+
</main>
|
|
354
|
+
</div>
|
|
355
|
+
</SidebarProvider>
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Create `src/components/app-sidebar.tsx` using shadcn/ui Sidebar components. Include:
|
|
361
|
+
- App logo/name at top
|
|
362
|
+
- Navigation links to each entity + settings
|
|
363
|
+
- User avatar + dropdown at bottom (with sign-out action)
|
|
364
|
+
|
|
365
|
+
### CRUD Pages (per entity)
|
|
366
|
+
|
|
367
|
+
Install components:
|
|
368
|
+
```bash
|
|
369
|
+
npx shadcn@latest add table dialog alert-dialog badge pagination select textarea
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
For each entity (e.g., `tasks`), create:
|
|
373
|
+
|
|
374
|
+
#### `src/app/(dashboard)/tasks/page.tsx` (list view)
|
|
375
|
+
- Server component that fetches initial data
|
|
376
|
+
- Client component `TasksTable` with:
|
|
377
|
+
- Data table using shadcn Table
|
|
378
|
+
- Search input
|
|
379
|
+
- "Create" button that opens a Dialog with form
|
|
380
|
+
- Row actions: edit, delete (with AlertDialog confirmation)
|
|
381
|
+
- Pagination
|
|
382
|
+
|
|
383
|
+
#### `src/hooks/use-tasks.ts` (data hook)
|
|
384
|
+
```typescript
|
|
385
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
|
|
386
|
+
import { createClient } from "@/lib/supabase/client"
|
|
387
|
+
import type { Task } from "@/types/database"
|
|
388
|
+
|
|
389
|
+
export function useTasks() {
|
|
390
|
+
const supabase = createClient()
|
|
391
|
+
return useQuery({
|
|
392
|
+
queryKey: ["tasks"],
|
|
393
|
+
queryFn: async () => {
|
|
394
|
+
const { data, error } = await supabase.from("tasks").select("*").order("created_at", { ascending: false })
|
|
395
|
+
if (error) throw error
|
|
396
|
+
return data as Task[]
|
|
397
|
+
},
|
|
398
|
+
})
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function useCreateTask() {
|
|
402
|
+
const supabase = createClient()
|
|
403
|
+
const queryClient = useQueryClient()
|
|
404
|
+
return useMutation({
|
|
405
|
+
mutationFn: async (values: Omit<Task, "id" | "created_at" | "user_id">) => {
|
|
406
|
+
const { data, error } = await supabase.from("tasks").insert(values).select().single()
|
|
407
|
+
if (error) throw error
|
|
408
|
+
return data
|
|
409
|
+
},
|
|
410
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["tasks"] }),
|
|
411
|
+
})
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function useUpdateTask() {
|
|
415
|
+
const supabase = createClient()
|
|
416
|
+
const queryClient = useQueryClient()
|
|
417
|
+
return useMutation({
|
|
418
|
+
mutationFn: async ({ id, ...values }: Partial<Task> & { id: string }) => {
|
|
419
|
+
const { data, error } = await supabase.from("tasks").update(values).eq("id", id).select().single()
|
|
420
|
+
if (error) throw error
|
|
421
|
+
return data
|
|
422
|
+
},
|
|
423
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["tasks"] }),
|
|
424
|
+
})
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function useDeleteTask() {
|
|
428
|
+
const supabase = createClient()
|
|
429
|
+
const queryClient = useQueryClient()
|
|
430
|
+
return useMutation({
|
|
431
|
+
mutationFn: async (id: string) => {
|
|
432
|
+
const { error } = await supabase.from("tasks").delete().eq("id", id)
|
|
433
|
+
if (error) throw error
|
|
434
|
+
},
|
|
435
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["tasks"] }),
|
|
436
|
+
})
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Replicate this pattern for every entity. The hook filename is always `use-<entity>.ts`.
|
|
441
|
+
|
|
442
|
+
### Settings Page
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
npx shadcn@latest add tabs switch
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
`src/app/(dashboard)/settings/page.tsx` — Tabs for Profile, Account, Notifications.
|
|
449
|
+
|
|
450
|
+
### Landing Page
|
|
451
|
+
|
|
452
|
+
```bash
|
|
453
|
+
npx shadcn@latest add button badge
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
`src/app/(marketing)/page.tsx` — Hero section, features grid (3-4 cards), pricing (if SaaS), CTA, footer. Use semantic HTML + Tailwind. Load the `frontend-design` skill for styling.
|
|
457
|
+
|
|
458
|
+
`src/app/(marketing)/layout.tsx` — Simple layout with navbar (logo + Login/Sign up buttons) + footer.
|
|
459
|
+
|
|
460
|
+
### Providers
|
|
461
|
+
|
|
462
|
+
#### `src/app/providers.tsx`
|
|
463
|
+
```tsx
|
|
464
|
+
"use client"
|
|
465
|
+
|
|
466
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
|
467
|
+
import { useState } from "react"
|
|
468
|
+
|
|
469
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
470
|
+
const [queryClient] = useState(() => new QueryClient({
|
|
471
|
+
defaultOptions: { queries: { staleTime: 60 * 1000 } },
|
|
472
|
+
}))
|
|
473
|
+
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Wrap the root `layout.tsx` body with `<Providers>`.
|
|
478
|
+
|
|
479
|
+
Run `npm run dev` — fix ALL errors before proceeding.
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## Phase 4: Database Schema
|
|
484
|
+
|
|
485
|
+
**Load the `supabase` skill.** Design tables for the entities from Phase 0.
|
|
486
|
+
|
|
487
|
+
For every table:
|
|
488
|
+
1. Include `user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL`
|
|
489
|
+
2. Include `created_at TIMESTAMPTZ DEFAULT now()`
|
|
490
|
+
3. Enable RLS: `ALTER TABLE public.<table> ENABLE ROW LEVEL SECURITY`
|
|
491
|
+
4. Create policies for SELECT, INSERT, UPDATE, DELETE using `auth.uid() = user_id`
|
|
492
|
+
|
|
493
|
+
Use MCP `execute_sql` if available, otherwise `supabase db query`.
|
|
494
|
+
|
|
495
|
+
Generate migration: `supabase db pull --local --yes`
|
|
496
|
+
|
|
497
|
+
Create TypeScript types in `src/types/database.ts` matching the schema.
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Phase 5: Design Polish
|
|
502
|
+
|
|
503
|
+
**Load the `frontend-design` skill.** Apply its principles to all generated pages:
|
|
504
|
+
- Choose a distinctive color palette (update `globals.css` / Tailwind theme)
|
|
505
|
+
- Typography: pick a characterful font pair from Google Fonts
|
|
506
|
+
- Micro-interactions: hover states, transitions, loading states
|
|
507
|
+
- Empty states: show helpful illustrations/text when no data exists
|
|
508
|
+
- Consistent spacing and visual rhythm
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Phase 6: Generate AGENTS.md
|
|
513
|
+
|
|
514
|
+
Create `AGENTS.md` in the project root. Adapt this template:
|
|
515
|
+
|
|
516
|
+
```markdown
|
|
517
|
+
# [App Name] — Agent Rules
|
|
518
|
+
|
|
519
|
+
## Stack
|
|
520
|
+
- Framework: Next.js 15 (App Router) + TypeScript
|
|
521
|
+
- Styling: Tailwind CSS + shadcn/ui
|
|
522
|
+
- Backend: Supabase (Database, Auth, RLS)
|
|
523
|
+
- Data: TanStack Query hooks + react-hook-form + zod
|
|
524
|
+
- Auth: @supabase/ssr with middleware token refresh
|
|
525
|
+
|
|
526
|
+
## Project Structure
|
|
527
|
+
- `src/app/(auth)/` — Login, signup, forgot-password, OAuth callback
|
|
528
|
+
- `src/app/(dashboard)/` — Protected pages (requires auth)
|
|
529
|
+
- `src/app/(marketing)/` — Public landing pages
|
|
530
|
+
- `src/components/ui/` — shadcn/ui components (do not edit manually)
|
|
531
|
+
- `src/hooks/use-<entity>.ts` — TanStack Query hooks per entity
|
|
532
|
+
- `src/lib/supabase/` — Supabase client (client.ts for browser, server.ts for server)
|
|
533
|
+
|
|
534
|
+
## Conventions
|
|
535
|
+
- **Add shadcn/ui components**: `npx shadcn@latest add <component-name>`
|
|
536
|
+
- **New entity CRUD**: Create `hooks/use-<entity>.ts` + `app/(dashboard)/<entity>/page.tsx`
|
|
537
|
+
- **Forms**: Always use react-hook-form + zod schema validation
|
|
538
|
+
- **Data fetching**: Always use TanStack Query hooks, never raw useEffect+fetch
|
|
539
|
+
- **Auth check**: Server components use `createClient()` from `@/lib/supabase/server`
|
|
540
|
+
- **Protected routes**: All pages in `(dashboard)` route group — middleware handles redirect
|
|
541
|
+
|
|
542
|
+
## Supabase
|
|
543
|
+
- MCP server configured in `opencode.json`
|
|
544
|
+
- **RLS is ON for all tables** — every new table MUST have RLS + policies
|
|
545
|
+
- **user_id pattern**: All user-owned tables have `user_id UUID REFERENCES auth.users(id)`
|
|
546
|
+
- **Migrations**: `supabase db pull --local --yes`
|
|
547
|
+
- **Never use `user_metadata`** for authorization
|
|
548
|
+
|
|
549
|
+
## Database Schema
|
|
550
|
+
[List tables and purposes — update as schema evolves]
|
|
551
|
+
|
|
552
|
+
## Environment
|
|
553
|
+
- `.env.local` — Supabase URL + anon key (gitignored)
|
|
554
|
+
- Never expose `service_role` key in client code
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## Phase 7: Iterative Verification
|
|
560
|
+
|
|
561
|
+
**DO NOT stop after scaffolding.** After each phase, run `npm run dev` and fix ALL errors.
|
|
562
|
+
|
|
563
|
+
After all phases complete, verify the full flow:
|
|
564
|
+
|
|
565
|
+
1. `npm run dev` starts clean — no errors, no warnings
|
|
566
|
+
2. Landing page loads at `/`
|
|
567
|
+
3. Navigate to `/login` — styled form renders
|
|
568
|
+
4. Click "Sign up" → signup page works
|
|
569
|
+
5. Sign up a test user — success
|
|
570
|
+
6. Log in with test user — redirects to dashboard
|
|
571
|
+
7. Dashboard shows sidebar + content area
|
|
572
|
+
8. Create an entity via the form — appears in list
|
|
573
|
+
9. Edit the entity — changes persist
|
|
574
|
+
10. Delete the entity — removed from list
|
|
575
|
+
11. Sign out works — redirects to login
|
|
576
|
+
12. `.env.local` is in `.gitignore`
|
|
577
|
+
13. `AGENTS.md` exists with correct content
|
|
578
|
+
|
|
579
|
+
**If ANY check fails, fix it and re-verify. Do not declare done until all checks pass.**
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
## Vite + React Variant
|
|
584
|
+
|
|
585
|
+
Only use this section if the user explicitly requests React+Vite instead of Next.js.
|
|
586
|
+
|
|
587
|
+
### Key differences:
|
|
588
|
+
- Scaffold: `npm create vite@latest <name> -- --template react-ts`
|
|
589
|
+
- Routing: `npm install react-router-dom` with `<BrowserRouter>` + `<Routes>`
|
|
590
|
+
- No server components — everything is client-side
|
|
591
|
+
- Supabase client: single `createClient()` (no SSR variant)
|
|
592
|
+
- No middleware — auth checks in route guards
|
|
593
|
+
- No route groups — use `pages/` directory convention
|
|
594
|
+
- Data fetching: TanStack Query only (no Server Actions)
|
|
595
|
+
|
|
596
|
+
### File structure:
|
|
597
|
+
```
|
|
598
|
+
src/
|
|
599
|
+
├── pages/
|
|
600
|
+
│ ├── auth/
|
|
601
|
+
│ │ ├── Login.tsx
|
|
602
|
+
│ │ ├── Signup.tsx
|
|
603
|
+
│ │ └── ForgotPassword.tsx
|
|
604
|
+
│ ├── dashboard/
|
|
605
|
+
│ │ ├── Dashboard.tsx
|
|
606
|
+
│ │ ├── Settings.tsx
|
|
607
|
+
│ │ └── <Entity>.tsx
|
|
608
|
+
│ └── Landing.tsx
|
|
609
|
+
├── components/
|
|
610
|
+
│ ├── ui/ # shadcn/ui
|
|
611
|
+
│ ├── Layout.tsx
|
|
612
|
+
│ └── ProtectedRoute.tsx
|
|
613
|
+
├── hooks/
|
|
614
|
+
│ └── use-<entity>.ts
|
|
615
|
+
├── lib/
|
|
616
|
+
│ └── supabase.ts # Single client
|
|
617
|
+
├── App.tsx # Router setup
|
|
618
|
+
└── main.tsx
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
Everything else (shadcn/ui, TanStack Query, react-hook-form, Supabase patterns) stays the same.
|