@jeiemgi/cckit 0.1.6
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/.claude-plugin/plugin.json +22 -0
- package/AGENTS.md +101 -0
- package/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/README.md +143 -0
- package/SECURITY.md +22 -0
- package/bin/cckit +215 -0
- package/cckit.config.json +34 -0
- package/commands/kit-add.md +42 -0
- package/commands/kit-docs.md +45 -0
- package/commands/kit-doctor.md +52 -0
- package/commands/kit-export-project.md +58 -0
- package/commands/kit-export-training.md +49 -0
- package/commands/kit-init.md +126 -0
- package/commands/kit-routines.md +59 -0
- package/commands/kit-update.md +132 -0
- package/docs/kit-annotate/01-explainer.html +225 -0
- package/docs/kit-annotate/02-implementation-plan.html +196 -0
- package/docs/media/.onboarding-capture.cast +5 -0
- package/docs/media/README.md +43 -0
- package/docs/media/build-demo.sh +63 -0
- package/docs/media/build-kit-init.sh +51 -0
- package/docs/media/build-onboarding.sh +51 -0
- package/docs/media/kit-dry-run.cast +107 -0
- package/docs/media/kit-dry-run.gif +0 -0
- package/docs/media/kit-init.cast +56 -0
- package/docs/media/kit-init.gif +0 -0
- package/docs/media/kit-onboarding.cast +148 -0
- package/docs/media/kit-onboarding.gif +0 -0
- package/githooks/pre-commit +18 -0
- package/kit.config.schema.json +105 -0
- package/package.json +54 -0
- package/privacy-denylist.example +8 -0
- package/profiles/automation.json +36 -0
- package/profiles/content.json +41 -0
- package/profiles/minimal.json +31 -0
- package/profiles/research.json +37 -0
- package/profiles/software.json +32 -0
- package/scripts/annotate-setup.sh +149 -0
- package/scripts/autopilot.sh +50 -0
- package/scripts/capture-project-ids.sh +53 -0
- package/scripts/check.sh +66 -0
- package/scripts/contribute.sh +48 -0
- package/scripts/debug.sh +54 -0
- package/scripts/init-upgrade-test.sh +99 -0
- package/scripts/init.sh +827 -0
- package/scripts/install.sh +24 -0
- package/scripts/kit-add-test.sh +62 -0
- package/scripts/kit-add.sh +115 -0
- package/scripts/kit-adopt-test.sh +61 -0
- package/scripts/kit-adopt.sh +122 -0
- package/scripts/kit-bump-version.sh +79 -0
- package/scripts/kit-digest.sh +126 -0
- package/scripts/kit-doctor.sh +663 -0
- package/scripts/kit-export-project-test.sh +82 -0
- package/scripts/kit-export-project.sh +245 -0
- package/scripts/kit-export-training-test.sh +51 -0
- package/scripts/kit-export-training.sh +175 -0
- package/scripts/kit-migrate-test.sh +80 -0
- package/scripts/kit-migrate.sh +190 -0
- package/scripts/kit-onboard-test.sh +63 -0
- package/scripts/kit-onboard.sh +69 -0
- package/scripts/kit-promote-test.sh +54 -0
- package/scripts/kit-promote.sh +102 -0
- package/scripts/kit-remove-test.sh +61 -0
- package/scripts/kit-remove.sh +84 -0
- package/scripts/kit-routines.sh +322 -0
- package/scripts/kit-version-check.sh +91 -0
- package/scripts/kit-wire-test.sh +54 -0
- package/scripts/kit-wire.sh +132 -0
- package/scripts/knowledge-lint.sh +96 -0
- package/scripts/lib/cckit-output.sh +36 -0
- package/scripts/lib/effort-metrics.sh +452 -0
- package/scripts/lib/effort-ops-test.sh +83 -0
- package/scripts/lib/effort-ops.sh +132 -0
- package/scripts/lib/effort-plan.sh +104 -0
- package/scripts/lib/effort.sh +191 -0
- package/scripts/lib/engine-adapter.sh +92 -0
- package/scripts/lib/gh-log.sh +58 -0
- package/scripts/lib/gh-project.sh +212 -0
- package/scripts/lib/handoff.sh +35 -0
- package/scripts/lib/kit-cli-test.sh +42 -0
- package/scripts/lib/kit-cli.sh +32 -0
- package/scripts/lib/kit-config-resolve.sh +145 -0
- package/scripts/lib/kit-config.sh +88 -0
- package/scripts/lib/kit-engine-test.sh +107 -0
- package/scripts/lib/kit-events.sh +62 -0
- package/scripts/lib/kit-gc.sh +117 -0
- package/scripts/lib/kit-interview-test.sh +77 -0
- package/scripts/lib/kit-interview.sh +203 -0
- package/scripts/lib/kit-local.sh +79 -0
- package/scripts/lib/kit-manifest.sh +127 -0
- package/scripts/lib/kit-mode-test.sh +49 -0
- package/scripts/lib/kit-mode.sh +67 -0
- package/scripts/lib/kit-operate.sh +105 -0
- package/scripts/lib/kit-profile-test.sh +62 -0
- package/scripts/lib/kit-profile.sh +115 -0
- package/scripts/lib/kit-task-ops-test.sh +63 -0
- package/scripts/lib/kit-task-ops.sh +341 -0
- package/scripts/lib/pr-evidence.sh +173 -0
- package/scripts/lib/project-scan.sh +16 -0
- package/scripts/lib/react-detect.sh +78 -0
- package/scripts/lib/role-identity.sh +47 -0
- package/scripts/lib/secret-guard.sh +96 -0
- package/scripts/lib/toon.sh +35 -0
- package/scripts/lib/ui.sh +42 -0
- package/scripts/lib/version-bump.sh +59 -0
- package/scripts/lib/worktree-issue-test.sh +45 -0
- package/scripts/lib/worktree-issue.sh +73 -0
- package/scripts/lib/worktree-start.sh +280 -0
- package/scripts/orchestrate.sh +160 -0
- package/scripts/portable-test.sh +53 -0
- package/scripts/publish.sh +94 -0
- package/scripts/setup-labels.sh +25 -0
- package/scripts/setup-milestones.sh +17 -0
- package/scripts/showcase.sh +64 -0
- package/scripts/status.sh +44 -0
- package/scripts/task-sync.sh +59 -0
- package/scripts/test.sh +48 -0
- package/scripts/web-install.sh +22 -0
- package/skills/kit-annotate/SKILL.md +107 -0
- package/skills/kit-autopilot/SKILL.md +108 -0
- package/skills/kit-contribute/SKILL.md +134 -0
- package/skills/kit-customize/SKILL.md +134 -0
- package/skills/kit-dev/SKILL.md +67 -0
- package/skills/kit-digest/SKILL.md +41 -0
- package/skills/kit-effort-close/SKILL.md +156 -0
- package/skills/kit-effort-new/SKILL.md +173 -0
- package/skills/kit-effort-pr/SKILL.md +139 -0
- package/skills/kit-effort-start/SKILL.md +85 -0
- package/skills/kit-gc/SKILL.md +80 -0
- package/skills/kit-onboard/SKILL.md +50 -0
- package/skills/kit-security-sweep/SKILL.md +57 -0
- package/skills/kit-ship/SKILL.md +43 -0
- package/skills/kit-task-close/SKILL.md +66 -0
- package/skills/kit-task-new/SKILL.md +51 -0
- package/skills/kit-task-pr/SKILL.md +43 -0
- package/skills/kit-task-pr-auto/SKILL.md +27 -0
- package/skills/kit-task-pr-merge/SKILL.md +53 -0
- package/skills/kit-task-start/SKILL.md +76 -0
- package/skills/kit-task-sync/SKILL.md +37 -0
- package/templates/CLAUDE.md.tmpl +106 -0
- package/templates/agents/analyst.md +55 -0
- package/templates/agents/auto-dev.md +93 -0
- package/templates/agents/backend.md +59 -0
- package/templates/agents/designer.md +73 -0
- package/templates/agents/devops.md +57 -0
- package/templates/agents/editor.md +48 -0
- package/templates/agents/frontend.md +81 -0
- package/templates/agents/generalist.md +46 -0
- package/templates/agents/local-delegate.md +70 -0
- package/templates/agents/n8n.md +65 -0
- package/templates/agents/pm.md +69 -0
- package/templates/agents/qa.md +66 -0
- package/templates/agents/researcher.md +57 -0
- package/templates/agents/security.md +65 -0
- package/templates/agents/tech-lead.md +75 -0
- package/templates/hooks/guard-base-branch-commit.sh.tmpl +45 -0
- package/templates/hooks/kit-local-status.sh.tmpl +34 -0
- package/templates/hooks/kit_version_check.sh.tmpl +6 -0
- package/templates/hooks/mempal_followup.sh.tmpl +97 -0
- package/templates/hooks/mempal_precompact.sh.tmpl +4 -0
- package/templates/hooks/mempal_save.sh.tmpl +4 -0
- package/templates/hooks/mempal_session_start.sh.tmpl +8 -0
- package/templates/hooks/prepush_gate.sh.tmpl +36 -0
- package/templates/hooks/repo-hygiene.sh.tmpl +72 -0
- package/templates/kit.config.json.tmpl +32 -0
- package/templates/knowledge-INDEX.md.tmpl +12 -0
- package/templates/lib/kit-sigil.sh.tmpl +124 -0
- package/templates/rules/branch-naming.md +104 -0
- package/templates/rules/communication-style.md +22 -0
- package/templates/rules/delegation-brief.md +40 -0
- package/templates/rules/design-routing.md +35 -0
- package/templates/rules/effort-model.md +122 -0
- package/templates/rules/knowledge-base.md +41 -0
- package/templates/rules/mempalace.md +110 -0
- package/templates/rules/plan-output-format.md +58 -0
- package/templates/rules/react-annotate.md +69 -0
- package/templates/rules/risk-tiered-review.md +62 -0
- package/templates/rules/skill-gaps.md +48 -0
- package/templates/rules/task-management.md +42 -0
- package/templates/settings/settings.local.json.tmpl +27 -0
- package/templates/skills/NAMESPACED +13 -0
- package/templates/skills/copywriting/SKILL.md +252 -0
- package/templates/skills/copywriting/references/copy-frameworks.md +344 -0
- package/templates/skills/copywriting/references/natural-transitions.md +272 -0
- package/templates/skills/feature-build-refine/SKILL.md +367 -0
- package/templates/skills/karpathy-guidelines/SKILL.md +69 -0
- package/templates/skills/morning-briefing/SKILL.md +46 -0
- package/templates/skills/speckit/SKILL.md +239 -0
- package/templates/skills/supabase-patterns/SKILL.md +88 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feature-build-refine
|
|
3
|
+
description: Feature/form/page build strategy for Next.js (App Router) + React + Supabase + RefineDev + shadcn projects. Self-gating — applies ONLY when RefineDev is in the stack.
|
|
4
|
+
when_to_use: "STACK-GATED. Use when building features, forms, components, or pages AND the project uses RefineDev — detect by checking package.json for @refinedev/* deps (e.g. @refinedev/core, @refinedev/react-hook-form, @refinedev/react-table). If RefineDev is NOT in package.json, this skill does NOT apply — ignore it. When it applies, follow it exactly; its conventions are non-negotiable for that stack."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Feature / Form / Page Build Strategy (Next.js + Supabase + RefineDev)
|
|
8
|
+
|
|
9
|
+
> **Applicability check first.** Read `package.json`. If there is no `@refinedev/*` dependency,
|
|
10
|
+
> STOP — this skill does not apply to this project. Otherwise follow it exactly. Replace
|
|
11
|
+
> `<model>` / `<feature>` with your domain entity. Supabase-specific data patterns are also
|
|
12
|
+
> covered by the `supabase-patterns` skill; they compose.
|
|
13
|
+
|
|
14
|
+
You are building features for a **Next.js 15 (App Router) + React 19 + TypeScript** app with this stack:
|
|
15
|
+
|
|
16
|
+
- **Backend:** Supabase (PostgreSQL + Auth + RLS)
|
|
17
|
+
- **Admin/CRUD framework:** RefineDev with custom data providers wrapping Supabase
|
|
18
|
+
- **UI:** shadcn-style component library in `src/components/ui/` (built with `cva` variants + a `cn()` clsx/tailwind-merge helper)
|
|
19
|
+
- **Styling:** TailwindCSS v4 with **semantic design tokens** (never hardcoded colors)
|
|
20
|
+
- **Forms:** React Hook Form (`@refinedev/react-hook-form`) + Zod validation
|
|
21
|
+
- **Tables:** TanStack Table via `@refinedev/react-table`
|
|
22
|
+
- **i18n:** i18next — every user-facing string goes through `t()`
|
|
23
|
+
- **Icons:** lucide-react, always with an explicit `className` for sizing
|
|
24
|
+
|
|
25
|
+
Follow the conventions below exactly. They are non-negotiable.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1. Feature Folder Architecture
|
|
30
|
+
|
|
31
|
+
Every domain entity is a **self-contained feature module** at `src/features/<model>/`. The folder is **flat** — the only allowed subdirectory is `components/`.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
src/features/<model>/
|
|
35
|
+
├── types.ts # Domain interfaces & type aliases ONLY (no constants, no fns, no React)
|
|
36
|
+
├── constants.ts # Label maps, option arrays, badge-variant maps, pure helper fns
|
|
37
|
+
├── actions.ts # "use server" — server actions calling Supabase (no types, no React)
|
|
38
|
+
├── components/
|
|
39
|
+
│ └── <model>-form.tsx # ONE unified Create/Edit form component
|
|
40
|
+
└── index.ts # Public barrel — re-exports types, constants, components
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**File responsibility table — enforce strictly:**
|
|
44
|
+
|
|
45
|
+
| File | Contains | Never contains |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `types.ts` | interfaces, type aliases | constants, functions, React |
|
|
48
|
+
| `constants.ts` | label maps, option lists, helpers, badge variants | types, server actions, React |
|
|
49
|
+
| `actions.ts` | `"use server"` functions | types, constants, React |
|
|
50
|
+
| `components/<model>-form.tsx` | React form component | business logic, DB calls |
|
|
51
|
+
| `index.ts` | re-exports | **never re-exports `actions.ts`** |
|
|
52
|
+
|
|
53
|
+
**Barrel rule:** `index.ts` exports the form component + its `FormData` type + `export * from "./types"` + `export * from "./constants"`. **Server actions are imported directly from `./actions`** — never through the barrel (keeps `"use server"` boundaries clean).
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// src/features/<model>/index.ts
|
|
57
|
+
export { ModelForm } from "./components/<model>-form";
|
|
58
|
+
export type { ModelFormData } from "./components/<model>-form";
|
|
59
|
+
export * from "./types";
|
|
60
|
+
export * from "./constants";
|
|
61
|
+
// DO NOT re-export from ./actions — server actions are imported directly
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Import discipline:**
|
|
65
|
+
|
|
66
|
+
- **Inside** the feature boundary, use **relative** imports (`"./types"`, `"../constants"`) — never `@/features/<model>/…`.
|
|
67
|
+
- **Outside** consumers import from the barrel: `import { ModelForm, FOO_LABELS } from "@/features/<model>"`.
|
|
68
|
+
- **No `src/lib/<model>/`** — feature-specific helpers live in the feature's `constants.ts`. `src/lib/` is only for truly generic, feature-agnostic utilities (`date.ts`, `utils.ts`, `supabase/`, `validations.ts`).
|
|
69
|
+
- **No shims** — when moving code, rewrite all import paths with `sed` and delete the old files. Never leave a re-export stub behind.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 2. The Unified Form Component (the core pattern)
|
|
74
|
+
|
|
75
|
+
There is **ONE component per feature** that handles both Create and Edit. A single `<model>Id?: string` prop drives everything: `isEdit = !!<model>Id`.
|
|
76
|
+
|
|
77
|
+
### Internal structure (in this order)
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
<model>-form.tsx
|
|
81
|
+
├── makeModelSchema(isEdit) → Zod schema factory; superRefine for mode-aware cross-field rules
|
|
82
|
+
├── ModelFormData (exported) → z.infer<ReturnType<typeof makeModelSchema>>
|
|
83
|
+
├── DEFAULT_VALUES → const covering EVERY field (shared baseline, both modes)
|
|
84
|
+
├── Payload (local, unexported) → type for the edit onFinish cast
|
|
85
|
+
└── ModelForm (exported) → ONE component, ONE return, isEdit drives all branching
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Schema: compose from shared primitives, never inline
|
|
89
|
+
|
|
90
|
+
Keep a `src/lib/validations.ts` of named Zod atoms (`zFullName`, `zEmail`, `zPhone`, `zPhoneOptional`, `zOptionalString`, `zCodigoPostal`, `zUrlOptional`, `zColorHex`, region/enum lists, etc.). **Never write `z.string().min(3).max(100)` when a named primitive exists.**
|
|
91
|
+
|
|
92
|
+
Mode-aware validation uses a **schema factory + `superRefine`**, not two separate schemas:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
function makeModelSchema(isEdit: boolean) {
|
|
96
|
+
return z.object({
|
|
97
|
+
// ── Shared ──
|
|
98
|
+
full_name: zFullName,
|
|
99
|
+
email: zEmail,
|
|
100
|
+
// ── Create-only ──
|
|
101
|
+
password: z.string().optional(),
|
|
102
|
+
confirm_password: z.string().optional(),
|
|
103
|
+
// ── Edit-only ──
|
|
104
|
+
status: publicStatusSchema.optional(),
|
|
105
|
+
change_password: z.boolean().default(false),
|
|
106
|
+
}).superRefine((data, ctx) => {
|
|
107
|
+
if (!isEdit) {
|
|
108
|
+
// create-mode cross-field validation (e.g. password match)
|
|
109
|
+
} else {
|
|
110
|
+
// edit-mode cross-field validation
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type ModelFormData = z.infer<ReturnType<typeof makeModelSchema>>;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`DEFAULT_VALUES` is a single const covering every field (used for both modes and as the spread base for edit values).
|
|
119
|
+
|
|
120
|
+
### Populate edit data via `values` — NEVER `useEffect + reset()`
|
|
121
|
+
|
|
122
|
+
This is critical. The fetched record arrives asynchronously; feed it through RHF's `values` prop, which re-initializes the form when its reference changes. `values: undefined` while loading keeps the form at `defaultValues`. `useEffect + reset()` is a banned anti-pattern (stale closures, fires after render).
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const isEdit = !!modelId;
|
|
126
|
+
const schema = useMemo(() => makeModelSchema(isEdit), [isEdit]); // stable; isEdit never changes
|
|
127
|
+
|
|
128
|
+
// Fetch existing record only in edit mode
|
|
129
|
+
const { query, result: record } = useOne<ModelProfile, HttpError>({
|
|
130
|
+
resource: "<table>",
|
|
131
|
+
id: modelId ?? "",
|
|
132
|
+
queryOptions: { enabled: isEdit },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Reactive edit values — undefined until the record arrives
|
|
136
|
+
const editValues: ModelFormData | undefined =
|
|
137
|
+
isEdit && record
|
|
138
|
+
? { ...DEFAULT_VALUES, full_name: record.full_name ?? "", /* …map fields… */ }
|
|
139
|
+
: undefined;
|
|
140
|
+
|
|
141
|
+
const {
|
|
142
|
+
refineCore: { onFinish, formLoading },
|
|
143
|
+
...form
|
|
144
|
+
} = useForm<BaseRecord, HttpError, ModelFormData>({
|
|
145
|
+
// Bridge Zod's input/output type split WITHOUT `as any`:
|
|
146
|
+
resolver: zodResolver(schema) as unknown as Resolver<ModelFormData>,
|
|
147
|
+
defaultValues: DEFAULT_VALUES,
|
|
148
|
+
values: editValues, // ← drives re-initialization; no useEffect
|
|
149
|
+
refineCoreProps: isEdit
|
|
150
|
+
? { resource: "<table>", action: "edit", id: modelId, redirect: false, onMutationSuccess: () => { /* router.push + refresh */ } }
|
|
151
|
+
: { action: "create" },
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Submit handling:** edit flow calls refine's `onFinish` (through the data provider); create flow calls the **server action directly**. When the payload type differs from the form type, cast as `(onFinish as unknown as (v: Payload) => Promise<void>)(payload)`.
|
|
156
|
+
|
|
157
|
+
### Loading / error guards, then ONE return
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
if (isEdit && query?.isLoading) return <SkeletonCard />;
|
|
161
|
+
if (isEdit && (query?.isError || !record)) return <ErrorCard />;
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<Form {...form}>
|
|
165
|
+
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-8">
|
|
166
|
+
<FormSection title={t("<feature>.sections.general")}>
|
|
167
|
+
<FormGrid cols={2}>
|
|
168
|
+
<TextField name="full_name" label={t("<feature>.fields.fullName")} icon={UserIcon} control={form.control} />
|
|
169
|
+
<TextField name="email" label={t("<feature>.fields.email")} icon={MailIcon} control={form.control} />
|
|
170
|
+
</FormGrid>
|
|
171
|
+
</FormSection>
|
|
172
|
+
|
|
173
|
+
<FormSection title={t("<feature>.sections.details")} separator>
|
|
174
|
+
<FormGrid cols={3}>{/* … */}</FormGrid>
|
|
175
|
+
</FormSection>
|
|
176
|
+
|
|
177
|
+
<FormSubmitActions isLoading={isLoading} isEdit={isEdit} onCancel={() => router.back()} />
|
|
178
|
+
</form>
|
|
179
|
+
</Form>
|
|
180
|
+
);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 3. Form Layout Primitives — always use, never ad-hoc
|
|
186
|
+
|
|
187
|
+
From `src/components/ui/form-grid.tsx`. **Never** write raw `<div className="grid grid-cols-X gap-Y">` for field rows.
|
|
188
|
+
|
|
189
|
+
- **`<FormGrid cols={1|2|3|4}>`** — responsive grid, collapses to 1 column on mobile.
|
|
190
|
+
- **`<FormSection title description separator>`** — labeled block; `separator` draws a divider above.
|
|
191
|
+
- **`<FormActions>`** — right-aligned submit/cancel row.
|
|
192
|
+
- **`<FormPanel title description action>`** — bordered row with descriptive text + an action button on the right (for non-field actions inside a form, e.g. "Send recovery email").
|
|
193
|
+
|
|
194
|
+
## 4. Form Field Primitives — `src/components/forms/`
|
|
195
|
+
|
|
196
|
+
These are **pure controlled components** (no internal state, no `useForm` inside them). Each wraps the `FormField → FormItem → FormLabel → FormControl → input → FormMessage` chain. Build them once, reuse everywhere:
|
|
197
|
+
|
|
198
|
+
| Pattern | Component |
|
|
199
|
+
|---|---|
|
|
200
|
+
| Label + Input (optional icon prefix) | `TextField` (pass `icon={IconComponent}` — **never** a manual wrapper div) |
|
|
201
|
+
| Label + Select dropdown | `SelectField` (accepts `Record`, `string[]`, or `{value,label}[]`; uses a `__none__` sentinel because Radix Select rejects empty-string values) |
|
|
202
|
+
| Label + Textarea (+ optional char-count) | `TextareaField` |
|
|
203
|
+
| Label + row of toggle buttons | `ToggleField` / `ToggleButtonGroup` (single or multiple) |
|
|
204
|
+
| Label + searchable multi-select with pills | `MultiSelectField` (wraps `MultiSelectCombobox` — Popover + Command) |
|
|
205
|
+
| Label + bordered checkbox + description | `CheckboxField` |
|
|
206
|
+
| Label + URL input + image preview | `ImageUrlField` |
|
|
207
|
+
| Cancel + Submit button row | `FormSubmitActions` (shows `Loader2` spinner, disables while loading, uses shared `buttons.*` i18n keys) |
|
|
208
|
+
| Anything appearing only once | inline `FormField` |
|
|
209
|
+
|
|
210
|
+
**Extraction rule:** only create a new reusable primitive when the exact raw pattern appears **3+ times** OR a matching primitive already exists. Don't add props for hypothetical future use — add only what the current form needs.
|
|
211
|
+
|
|
212
|
+
**Array/controlled fields:** always use `render={({ field }) => …}` and pass `field.value` / `field.onChange`. Never reach for `form.getValues` / `form.setValue` on a controlled field.
|
|
213
|
+
|
|
214
|
+
Example `TextField` (the canonical primitive shape — generic over `FieldValues`, forwards native input props):
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
interface TextFieldProps<T extends FieldValues>
|
|
218
|
+
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "name"> {
|
|
219
|
+
control: Control<T>;
|
|
220
|
+
name: FieldPath<T>;
|
|
221
|
+
label: string;
|
|
222
|
+
icon?: ComponentType<{ className?: string }>;
|
|
223
|
+
description?: React.ReactNode;
|
|
224
|
+
}
|
|
225
|
+
// Renders: FormField → FormItem → FormLabel → FormControl → (icon ? relative-wrapped Input.pl-10 : Input) → FormMessage
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 5. List Pages (DataTable)
|
|
231
|
+
|
|
232
|
+
Built on TanStack Table via `@refinedev/react-table` + a `DataTable` wrapper in `src/components/refine-ui/data-table/`.
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
const columns = useMemo<ColumnDef<Model>[]>(() => [ /* … */ ], [router]);
|
|
236
|
+
|
|
237
|
+
const table = useTable<Model>({
|
|
238
|
+
columns,
|
|
239
|
+
refineCoreProps: {
|
|
240
|
+
resource: "<table>",
|
|
241
|
+
filters: { permanent: [{ field: "role", operator: "in", value: [...] }] },
|
|
242
|
+
syncWithLocation: true,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const { tableQuery } = table.refineCore; // NOT tableQueryResult
|
|
247
|
+
const data = tableQuery.data?.data ?? [];
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Column rules:
|
|
251
|
+
|
|
252
|
+
- **Wrap columns in `useMemo`.** Every column has an `id` and a `size` (px width).
|
|
253
|
+
- Use `id` / `accessorKey` / `header` / `cell` (TanStack), **not** `dataIndex` / `render` / `width` (legacy Ant).
|
|
254
|
+
- Headers compose `<DataTableSorter>`, `<DataTableFilterDropdownText>`, `<DataTableFilterCombobox>` (multi-select), `<DataTableFilterDropdownNumeric>`.
|
|
255
|
+
- Action columns set `enableSorting: false` + `enableColumnFilter: false`.
|
|
256
|
+
- Cells use `DataTableTooltip` (hover info), `DataTableBadgeList` (overflowing tag lists), `truncate` for long text.
|
|
257
|
+
- Parent container: `min-w-0`; table container `overflow-x-auto`; `minWidth` = sum of column sizes.
|
|
258
|
+
|
|
259
|
+
Page shell uses `<ListView>` / `<ListViewHeader>` + optional `<ListViewStatistics>` for summary cards.
|
|
260
|
+
|
|
261
|
+
Recommended column sizes: ID 60–80px · Status badge 100–120px · short text 150–200px · name/title 200–300px · email 200–250px · date 100–120px · actions 60–100px.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 6. Route Pages — thin wiring only
|
|
266
|
+
|
|
267
|
+
Pages are thin: they pick a View shell and render the feature form/list. No business logic.
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
// <route>/create/page.tsx
|
|
271
|
+
"use client";
|
|
272
|
+
import { ModelForm } from "@/features/<model>";
|
|
273
|
+
import { CreateView } from "@/components/refine-ui/views/create-view";
|
|
274
|
+
|
|
275
|
+
export default function CreateModelPage() {
|
|
276
|
+
return <CreateView title="Nuevo Model"><ModelForm /></CreateView>;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// <route>/[id]/edit/page.tsx
|
|
280
|
+
"use client";
|
|
281
|
+
import { use } from "react";
|
|
282
|
+
import { ModelForm } from "@/features/<model>";
|
|
283
|
+
import { EditView } from "@/components/refine-ui/views/edit-view";
|
|
284
|
+
|
|
285
|
+
export default function ModelEditPage({ params }: { params: Promise<{ id: string }> }) {
|
|
286
|
+
const { id } = use(params); // React 19: params is a Promise, unwrap with use()
|
|
287
|
+
return <EditView title="Editar Model"><ModelForm modelId={id} /></EditView>;
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
View shells: `CreateView` / `EditView` / `ListView` / `ShowView` (+ their `*Header` variants). Use the canonical path `<route>/[id]/edit/page.tsx` (not `edit/[id]`).
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## 7. Server Actions
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// src/features/<model>/actions.ts
|
|
299
|
+
"use server";
|
|
300
|
+
import { createClient } from "@/lib/supabase/server";
|
|
301
|
+
import type { CreateModelInput } from "./types";
|
|
302
|
+
|
|
303
|
+
async function requireRole() { // shared auth guard at top of file
|
|
304
|
+
const supabase = await createClient();
|
|
305
|
+
const { data: { user } } = await supabase.auth.getUser();
|
|
306
|
+
if (!user) throw new Error("No autorizado");
|
|
307
|
+
// …role check via profiles table…
|
|
308
|
+
return { supabase, userId: user.id };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export async function createModelAction(input: CreateModelInput): Promise<{ id: string }> {
|
|
312
|
+
const { supabase } = await requireRole();
|
|
313
|
+
const { data, error } = await supabase.from("<table>").insert(input).select("id").single();
|
|
314
|
+
if (error) throw error;
|
|
315
|
+
return { id: data.id };
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Three Supabase clients, by context (see the `supabase-patterns` skill for detail):
|
|
320
|
+
|
|
321
|
+
- `@/lib/supabase/client` — browser / client components
|
|
322
|
+
- `@/lib/supabase/server` — server components, route handlers, server actions
|
|
323
|
+
- `@/lib/supabase/admin` — service-role operations (`createAdminClient`)
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 8. Styling & Tokens
|
|
328
|
+
|
|
329
|
+
- **Semantic tokens only:** `bg-background`, `bg-card`, `bg-muted`, `bg-accent`, `border-border`, `text-foreground`, `text-muted-foreground`, `bg-primary`, `bg-destructive`, etc. These make light/dark themes work. Never hardcode `text-white`, `bg-gray-800`, hex values, or inline `style={{}}`.
|
|
330
|
+
- Brand color used sparingly (`text-brand-*`).
|
|
331
|
+
- Layout with Tailwind grid/flex utilities — never Ant-style `Row`/`Col`.
|
|
332
|
+
- Icons: lucide `*Icon` names, always sized (`<UserIcon className="h-4 w-4" />`).
|
|
333
|
+
- Use `cn()` for conditional classes, not template-string concatenation.
|
|
334
|
+
- Date formatting: a shared `src/lib/date.ts` (`formatDateShort`, `formatDateMedium`, `formatDateTime`, `formatMonthYear`) — never inline `toLocaleDateString` in feature code.
|
|
335
|
+
|
|
336
|
+
### UI craft constraints
|
|
337
|
+
|
|
338
|
+
- `motion/react` for JS animation, `tw-animate-css` for CSS animation.
|
|
339
|
+
- Animate **only** `transform` and `opacity`, never layout properties. Interaction feedback ≤ 200ms.
|
|
340
|
+
- Radix primitives for keyboard/focus behavior; `AlertDialog` for destructive actions.
|
|
341
|
+
- `text-balance` for headings, `tabular-nums` for data. `h-dvh` not `h-screen`. No gradients unless explicitly requested.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## 9. i18n
|
|
346
|
+
|
|
347
|
+
Every user-facing string goes through `t()` from `useTranslation()`. Keys live in `public/locales/<lang>/common.json` under a namespace matching the feature name. No hardcoded display strings in JSX. Use shared keys for generic buttons (`buttons.cancel`, `buttons.save`, `buttons.create`).
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## 10. Non-Negotiable Rules (checklist)
|
|
352
|
+
|
|
353
|
+
1. Feature folder is **flat** — only `components/` subdir allowed.
|
|
354
|
+
2. File names are **dashed-case** (`model-form.tsx`, `constants.ts`).
|
|
355
|
+
3. **No `src/lib/<model>/`** — feature code goes in `constants.ts`.
|
|
356
|
+
4. **No shims** — rewrite imports with `sed`, delete old files.
|
|
357
|
+
5. **No `as any`** — use `as unknown as SpecificType` with a comment.
|
|
358
|
+
6. **Relative imports inside the feature**, barrel imports outside.
|
|
359
|
+
7. **`actions.ts` never re-exported** from `index.ts`.
|
|
360
|
+
8. **Shared date formatting** via `@/lib/date`.
|
|
361
|
+
9. **Form layout** always via `FormGrid`/`FormSection`/`FormActions` — no ad-hoc grids.
|
|
362
|
+
10. **Controlled fields** use `field.value`/`field.onChange`, never `getValues`/`setValue`.
|
|
363
|
+
11. **`values` not `useEffect + reset()`** for edit population.
|
|
364
|
+
12. **No constants in `.tsx`** — they belong in `constants.ts`; check shared validations first.
|
|
365
|
+
13. **Compose Zod from shared primitives**, don't duplicate inline chains.
|
|
366
|
+
14. **All strings through `t()`**.
|
|
367
|
+
15. **`TextField` icon via `icon` prop**, never manual wrapper divs.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: karpathy-guidelines
|
|
3
|
+
description: Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Karpathy Guidelines
|
|
8
|
+
|
|
9
|
+
Behavioral guidelines to reduce common LLM coding mistakes, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls.
|
|
10
|
+
|
|
11
|
+
> Vendored into claude-kit from [multica-ai/andrej-karpathy-skills](https://github.com/multica-ai/andrej-karpathy-skills) (MIT, © forrestchang). Available in every profile — it applies to all developers.
|
|
12
|
+
|
|
13
|
+
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
|
14
|
+
|
|
15
|
+
## 1. Think Before Coding
|
|
16
|
+
|
|
17
|
+
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
|
18
|
+
|
|
19
|
+
Before implementing:
|
|
20
|
+
- State your assumptions explicitly. If uncertain, ask.
|
|
21
|
+
- If multiple interpretations exist, present them - don't pick silently.
|
|
22
|
+
- If a simpler approach exists, say so. Push back when warranted.
|
|
23
|
+
- If something is unclear, stop. Name what's confusing. Ask.
|
|
24
|
+
|
|
25
|
+
## 2. Simplicity First
|
|
26
|
+
|
|
27
|
+
**Minimum code that solves the problem. Nothing speculative.**
|
|
28
|
+
|
|
29
|
+
- No features beyond what was asked.
|
|
30
|
+
- No abstractions for single-use code.
|
|
31
|
+
- No "flexibility" or "configurability" that wasn't requested.
|
|
32
|
+
- No error handling for impossible scenarios.
|
|
33
|
+
- If you write 200 lines and it could be 50, rewrite it.
|
|
34
|
+
|
|
35
|
+
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
|
36
|
+
|
|
37
|
+
## 3. Surgical Changes
|
|
38
|
+
|
|
39
|
+
**Touch only what you must. Clean up only your own mess.**
|
|
40
|
+
|
|
41
|
+
When editing existing code:
|
|
42
|
+
- Don't "improve" adjacent code, comments, or formatting.
|
|
43
|
+
- Don't refactor things that aren't broken.
|
|
44
|
+
- Match existing style, even if you'd do it differently.
|
|
45
|
+
- If you notice unrelated dead code, mention it - don't delete it.
|
|
46
|
+
|
|
47
|
+
When your changes create orphans:
|
|
48
|
+
- Remove imports/variables/functions that YOUR changes made unused.
|
|
49
|
+
- Don't remove pre-existing dead code unless asked.
|
|
50
|
+
|
|
51
|
+
The test: Every changed line should trace directly to the user's request.
|
|
52
|
+
|
|
53
|
+
## 4. Goal-Driven Execution
|
|
54
|
+
|
|
55
|
+
**Define success criteria. Loop until verified.**
|
|
56
|
+
|
|
57
|
+
Transform tasks into verifiable goals:
|
|
58
|
+
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
|
59
|
+
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
|
60
|
+
- "Refactor X" → "Ensure tests pass before and after"
|
|
61
|
+
|
|
62
|
+
For multi-step tasks, state a brief plan:
|
|
63
|
+
```
|
|
64
|
+
1. [Step] → verify: [check]
|
|
65
|
+
2. [Step] → verify: [check]
|
|
66
|
+
3. [Step] → verify: [check]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: morning-briefing
|
|
3
|
+
description: Daily briefing — what's active on the board, what's blocked, what to focus on.
|
|
4
|
+
when_to_use: When the owner starts their day or asks for a morning briefing / daily summary.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Morning Briefing
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
Reads GitHub (source of truth) + active plans, then delivers a concise briefing. Reads repo + plans dir from `.claude/kit.config.json`.
|
|
12
|
+
|
|
13
|
+
## How to run
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
source scripts/lib/kit-config.sh && load_kit_config
|
|
17
|
+
gh issue list --repo "$KIT_REPO" --state open --json number,title,labels,assignees --limit 30
|
|
18
|
+
# then scan $KIT_PLANS_DIR for active plans (anything not in archive/) if plans are enabled
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then produce:
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
**Buenos días.** · **Today is [day], [date].**
|
|
26
|
+
|
|
27
|
+
### Urgent
|
|
28
|
+
- [p1 issues open or blocked]
|
|
29
|
+
|
|
30
|
+
### Focus today
|
|
31
|
+
- [top 1–3 In Progress or unblocked items]
|
|
32
|
+
|
|
33
|
+
### On the radar
|
|
34
|
+
- [upcoming Todo items or active plans]
|
|
35
|
+
|
|
36
|
+
### Active plans
|
|
37
|
+
- [active plans by title; skip if none]
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Rules
|
|
42
|
+
|
|
43
|
+
- No long intros — straight to the briefing
|
|
44
|
+
- Bullets only · keep under 30 lines · skip empty sections
|
|
45
|
+
- Language: match the owner (see `.claude/rules/communication-style.md`)
|
|
46
|
+
- Source of truth is GitHub — never invent task state
|