@3lineas/grooveskills 1.0.0 → 1.0.3

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.
@@ -0,0 +1,129 @@
1
+ # RSC / `'use client'` Boundary & `'use server'` Security
2
+
3
+ > Source of truth: [Server Components](https://react.dev/reference/rsc/server-components), [`'use client'`](https://react.dev/reference/rsc/use-client), and [`'use server'`](https://react.dev/reference/rsc/use-server).
4
+
5
+ ## Server Components by Default
6
+
7
+ In an RSC architecture, components render on the server ahead of time and emit a serializable UI tree:
8
+
9
+ - **No client state or effects**: Server Components cannot call `useState`, `useEffect`, or browser DOM APIs.
10
+ - **Direct `async / await`**: Server Components can `await` database/action calls directly without shipping library code to the browser.
11
+
12
+ ```tsx
13
+ import { getArticleByIdAction } from "@/actions/articles/get-article-by-id.action";
14
+ import { LikeButton } from "@/components/articles/like-button";
15
+
16
+ export async function ArticleView({ articleId }: { articleId: string }) {
17
+ const article = await getArticleByIdAction(articleId);
18
+ if (!article) {
19
+ return <p>The requested article could not be found.</p>;
20
+ }
21
+
22
+ return (
23
+ <article>
24
+ <h1>{article.title}</h1>
25
+ <LikeButton articleId={article.id} initialLikes={article.likes} />
26
+ </article>
27
+ );
28
+ }
29
+ ```
30
+
31
+ ## The Boundary Is on the Module Graph, Not the Render Tree
32
+
33
+ `'use client'` placed at the top of a file marks that **module and all of its static imports** as client code.
34
+
35
+ ### Two Key Architectural Rules
36
+
37
+ 1. **Props crossing into a Client Component must be serializable** (primitives, plain objects, arrays, `ReactNode`, or `'use server'` functions). Class instances, closures, and `Date`/`Map` instances (unless supported by the framework serializer) cannot cross as props.
38
+ 2. **Client Components can render Server Components passed as `children` (Slot pattern).** Passing a Server Component via `children` keeps the server subtree on the server without pulling its dependencies into the client bundle:
39
+
40
+ ```tsx
41
+ "use client";
42
+
43
+ import { useState, type ReactNode } from "react";
44
+
45
+ export function CollapsiblePanel({
46
+ title,
47
+ children,
48
+ }: {
49
+ title: string;
50
+ children: ReactNode;
51
+ }) {
52
+ const [isOpen, setIsOpen] = useState<boolean>(false);
53
+
54
+ return (
55
+ <section>
56
+ <button type="button" onClick={() => setIsOpen((prev) => !prev)}>
57
+ {title}
58
+ </button>
59
+ {isOpen && <div>{children}</div>}
60
+ </section>
61
+ );
62
+ }
63
+ ```
64
+
65
+ ## `'use server'` — Server Functions & Valibot Validation
66
+
67
+ `'use server'` marks a function as callable from the client but executed on the server. Treat every `'use server'` function as an **untrusted public HTTP endpoint**:
68
+
69
+ 1. **Always validate inputs with Valibot** (`* as v from "valibot"`). Never use Zod.
70
+ 2. **Always authenticate and authorize** the caller inside the function before mutating state.
71
+
72
+ ```tsx
73
+ "use server";
74
+
75
+ import * as v from "valibot";
76
+ import { requireSessionUser } from "@/actions/auth/require-session-user.action";
77
+ import { createCommentAction } from "@/actions/comments/create-comment.action";
78
+
79
+ const publishCommentSchema = v.object({
80
+ text: v.pipe(
81
+ v.string("Please enter a comment."),
82
+ v.trim(),
83
+ v.minLength(2, "Your comment must be at least 2 characters long.")
84
+ ),
85
+ });
86
+
87
+ export async function publishCommentServerFn(formData: FormData) {
88
+ const parsed = v.safeParse(publishCommentSchema, {
89
+ text: formData.get("text"),
90
+ });
91
+
92
+ if (!parsed.success) {
93
+ return {
94
+ success: false,
95
+ error: parsed.issues[0].message,
96
+ };
97
+ }
98
+
99
+ const user = await requireSessionUser();
100
+ await createCommentAction({ userId: user.id, text: parsed.output.text });
101
+
102
+ return {
103
+ success: true,
104
+ message: "Your comment has been published successfully.",
105
+ };
106
+ }
107
+ ```
108
+
109
+ ## Version Pinning in RSC Projects
110
+
111
+ RSC relies on bundler-facing internals that are not semver-stable across minor/patch skews. Pin exact matching versions of `react` and `react-dom` in `package.json`:
112
+
113
+ ```json
114
+ {
115
+ "dependencies": {
116
+ "react": "19.2.0",
117
+ "react-dom": "19.2.0"
118
+ }
119
+ }
120
+ ```
121
+
122
+ ## Anti-Patterns
123
+
124
+ | Anti-pattern | Why it is wrong | Fix |
125
+ |---|---|---|
126
+ | Calling `useState`/`useEffect` in a Server Component | Server Components do not execute on the client | Extract interactive leaf into a `'use client'` component |
127
+ | Importing server-only modules into a `'use client'` file | Pulls server code into the client bundle or fails build | Pass server data via serializable props or `children` slots |
128
+ | Trusting `'use server'` arguments without validation | Client payloads can be forged like any HTTP POST | Validate with Valibot (`v.safeParse`) and verify auth inside the function |
129
+ | Using caret ranges (`^19.0.0`) with RSC bundlers | Version skew between `react` and `react-dom` breaks RSC wiring | Pin exact matching versions |
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: react-core
3
+ description: Use when building or modifying React components, JSX, props, local state, core hooks (useState, useEffect, useRef, useContext, useMemo, useCallback), lists, conditional UI, or controlled inputs. TRIGGER when the project imports 'react' or 'react-dom', or when editing .tsx/.jsx files. Always adhere to the 'best-practices' skill and pair with 'react-advanced' for React 19 platform features and performance architecture.
4
+ ---
5
+
6
+ # React Core Fundamentals & Opinionated Standards
7
+
8
+ Canonical reference for React fundamentals in GrooveSkills projects: function components, JSX, props and immutable state, core hooks, composition, conditional and list rendering, and controlled inputs.
9
+
10
+ ---
11
+
12
+ ## 1. Golden Rules (Non-negotiable)
13
+
14
+ 1. **One Component per File & < 200 Lines**:
15
+ - Every exported React component resides in its own dedicated `.tsx` file and never exceeds **200 lines**.
16
+ - Extract sub-components or custom hooks (`use-*.ts`) into atomic files before reaching the limit.
17
+ 2. **Mandatory Path Aliases (`@/...`) & Zero Barrel Files**:
18
+ - Import components, hooks, schemas, and utilities directly via path aliases (`@/components/ui/card`, `@/hooks/use-window-width`).
19
+ - Never create `index.ts` or `index.tsx` barrel files to re-export components or hooks.
20
+ 3. **Strict Props Typing (Zero `any`)**:
21
+ - Define explicit `interface` or `type` declarations for component props.
22
+ - Treat `props` and `state` as strictly immutable.
23
+ 4. **Early Returns for UI States (No Nested Ternaries)**:
24
+ - Handle loading, error, and empty states with top-level guard clauses (`if (!data) return <EmptyState />;`) instead of deeply nested JSX conditionals.
25
+ 5. **Top-Level Hooks Only (Rules of Hooks)**:
26
+ - Call hooks unconditionally at the top level of function components or custom hooks—never inside loops, conditions, or nested functions (except React 19's `use()`, covered in `react-advanced`).
27
+ 6. **Immutable State Updates & Functional Updaters**:
28
+ - Never mutate arrays or objects in place (`items.push()`). Always pass a new reference (`[...items, next]`).
29
+ - Use functional updaters (`setCount((prev) => prev + step)`) whenever next state depends on previous state.
30
+ 7. **Compiler-Aware Memoization**:
31
+ - Do not wrap every value or callback in `useMemo` or `useCallback` by reflex. When the React Compiler is active, it memoizes automatically.
32
+ - Reserve manual `useMemo` and `useCallback` for stabilizing references passed to `useEffect` dependency arrays or for genuinely expensive computations in uncompiled modules.
33
+ 8. **Stable Identity Keys in Lists**:
34
+ - Always provide a stable, unique `key` derived from domain identity (`item.id`), never the array index (`i`) for dynamic or reorderable lists.
35
+ 9. **Controlled Inputs with Single Source of Truth**:
36
+ - Derive input `value` from state and handle updates via `onChange`, validating user input with **Valibot** (`* as v from "valibot"`) where applicable.
37
+
38
+ ---
39
+
40
+ ## 2. Components, JSX, Props, and Early Returns
41
+
42
+ Name components in `PascalCase`. Return a single root element or Fragment (`<>...</>`). Use guard clauses to keep the primary render path flat.
43
+
44
+ ```tsx
45
+ import { useState } from "react";
46
+
47
+ interface UserSummaryProps {
48
+ name: string;
49
+ role?: string;
50
+ isActive: boolean;
51
+ step?: number;
52
+ }
53
+
54
+ export function UserSummary({ name, role = "Member", isActive, step = 1 }: UserSummaryProps) {
55
+ const [count, setCount] = useState<number>(0);
56
+
57
+ if (!isActive) {
58
+ return <p className="text-muted">This account is currently inactive.</p>;
59
+ }
60
+
61
+ return (
62
+ <section className="user-summary">
63
+ <h2>Hello, {name} ({role})</h2>
64
+ <button type="button" onClick={() => setCount((prev) => prev + step)}>
65
+ Count: {count}
66
+ </button>
67
+ </section>
68
+ );
69
+ }
70
+ ```
71
+
72
+ ---
73
+
74
+ ## 3. Core Hooks Guide
75
+
76
+ - **`useState`**: Holds reactive local state (`const [isOpen, setIsOpen] = useState<boolean>(false);`).
77
+ - **`useEffect`**: Synchronizes with external systems (DOM listeners, timers, subscriptions). Always declare accurate dependencies and return a cleanup function.
78
+ - **`useRef`**: Holds a mutable value across renders without causing re-renders, or references a DOM node.
79
+ - **`useMemo` & `useCallback`**: Deliberate escape hatches to stabilize references for `useEffect` dependency arrays or heavy computations in uncompiled components.
80
+ - **`useContext`**: Reads shared state from a `Context` higher in the tree (`<ThemeContext value={theme}>` in React 19).
81
+
82
+ ```tsx
83
+ import { useEffect, useRef, useState } from "react";
84
+
85
+ export function AutoFocusWindowTracker() {
86
+ const inputRef = useRef<HTMLInputElement>(null);
87
+ const [width, setWidth] = useState<number>(() => window.innerWidth);
88
+
89
+ useEffect(() => {
90
+ inputRef.current?.focus();
91
+ const handleResize = (): void => setWidth(window.innerWidth);
92
+ window.addEventListener("resize", handleResize);
93
+ return () => window.removeEventListener("resize", handleResize);
94
+ }, []);
95
+
96
+ return <input ref={inputRef} placeholder={`Viewport: ${width}px`} />;
97
+ }
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 4. Composition, Lists, and Controlled Inputs
103
+
104
+ Favor composition via `children` over bloated configuration props, use stable domain keys (`todo.id`) for lists, and bind controlled inputs to state:
105
+
106
+ ```tsx
107
+ import { useState, type ReactNode } from "react";
108
+
109
+ export interface TodoItem {
110
+ id: string;
111
+ text: string;
112
+ done: boolean;
113
+ }
114
+
115
+ export function TodoCard({
116
+ title,
117
+ todos,
118
+ children,
119
+ }: {
120
+ title: string;
121
+ todos: TodoItem[];
122
+ children?: ReactNode;
123
+ }) {
124
+ const [filter, setFilter] = useState<string>("");
125
+
126
+ if (todos.length === 0) {
127
+ return <p>Your task list is clear. Add a new item to get started.</p>;
128
+ }
129
+
130
+ const visible = todos.filter((t) => t.text.toLowerCase().includes(filter.toLowerCase()));
131
+
132
+ return (
133
+ <article className="card">
134
+ <h2>{title}</h2>
135
+ <input
136
+ value={filter}
137
+ onChange={(event) => setFilter(event.target.value)}
138
+ placeholder="Filter tasks..."
139
+ />
140
+ <ul>
141
+ {visible.map((todo) => (
142
+ <li key={todo.id}>{todo.done ? <s>{todo.text}</s> : todo.text}</li>
143
+ ))}
144
+ </ul>
145
+ {children}
146
+ </article>
147
+ );
148
+ }
149
+ ```
150
+
151
+ ---
152
+
153
+ ## 5. Anti-Patterns to Avoid
154
+
155
+ | Anti-Pattern | Why It Fails | Correct Pattern |
156
+ | :--- | :--- | :--- |
157
+ | Mutating state in place (`items.push(next)`) | Same array reference; React skips re-render | Create a new reference: `setItems((prev) => [...prev, next])` |
158
+ | Calling hooks after `if` or inside loops | Breaks React's deterministic hook call order | Call hooks unconditionally at the top level; branch on results |
159
+ | Using array index `key={i}` on dynamic lists | Misattributes component state on insert, delete, or sort | Use a stable domain identifier: `key={item.id}` |
160
+ | Fetching or deriving synchronous state inside `useEffect` | Triggers an extra render pass and causes UI flicker | Derive values inline during render; use `use()` / Suspense or Actions for async data (`react-advanced`) |
161
+ | Re-exporting components via `components/index.ts` | Violates Zero Barrel Files and harms tree-shaking | Import directly from `@/components/ui/card` |
162
+
163
+ ---
164
+
165
+ ## 6. Sibling Skills
166
+
167
+ - **`best-practices`**: Universal clean architecture, `< 200` lines limit, `@/` path aliases, strict typing, and courteous user feedback.
168
+ - **`react-advanced`**: React 19 platform features, React Compiler 1.0 auto-memoization, concurrent rendering (`useTransition`, `useDeferredValue`, `Suspense`), `use()`, Actions (`useActionState`, `useOptimistic`, `useFormStatus`), context optimization, virtualization, and RSC boundaries.
@@ -0,0 +1,187 @@
1
+ ---
2
+ name: react-create-page
3
+ description: Use when creating, refactoring, or extending React pages, views, screens, dashboards, or forms. Enforces clean page architecture with zero logic in page components, domain custom hooks, isolated API actions, atomic co-location, Valibot validation, and @formisch/react forms. TRIGGER when creating or modifying files in src/pages/ or src/features/, or when building React views with API actions. Always adhere to 'best-practices' and pair with 'react-core' and 'react-advanced'.
4
+ ---
5
+
6
+ # React Page & View Architecture (`react-create-page`)
7
+
8
+ Standardized architectural pattern for building maintainable, enterprise-grade React pages and views. Enforces declarative page coordinators, isolated domain hooks, pure API actions, atomic co-location, and type-safe forms.
9
+
10
+ ---
11
+
12
+ ## 1. Golden Architectural Rules (Non-negotiable)
13
+
14
+ 1. **Zero Logic in Page Components (`use<Domain>Page` Hook)**:
15
+ - The main page component (`<domain>-page.tsx` or `page.tsx`) must be strictly declarative.
16
+ - **All state, effects, transitions, and submit handlers must live in a dedicated domain custom hook** (`use-<domain>-page.ts`).
17
+ - The page component only calls the hook and passes state/handlers to atomic subcomponents.
18
+ 2. **Early Returns for UI States in Page**:
19
+ - Handle loading, error, and empty states at the top level with guard clauses (`if (isLoading) return <PageSkeleton />;`).
20
+ - Keep the main render tree flat; never use nested ternaries.
21
+ 3. **Domain Co-location of Components, Hooks & Types**:
22
+ - Each feature/page lives in its own domain directory (`src/pages/<domain>/` or `src/features/<domain>/`).
23
+ - Subcomponents exclusive to the page reside in `components/`, hooks in `hooks/`, and domain types/schemas in `types/` or `schemas/`.
24
+ 4. **Mandatory Atomization & Hard 200-Line Limit**:
25
+ - Break headers, KPI summaries, filter bars, tables, cards, and form sections into dedicated atomic files under `components/`.
26
+ - Every file must be under **200 lines**.
27
+ 5. **No Prop Drilling (Consume Hooks Directly)**:
28
+ - If an atomic component requires deep state, let it consume the domain hook or a domain context directly instead of passing props through intermediary components.
29
+ 6. **API Communication Exclusively via Domain Actions (`src/actions/<domain>/`)**:
30
+ - Direct `fetch` calls or raw HTTP requests inside components or hooks are **strictly forbidden**.
31
+ - Encapsulate all network requests in pure async action functions under `src/actions/<domain>/<action-name>.action.ts` returning typed `ActionResponse<T>`.
32
+ 7. **Zero Mocks in Production Views**:
33
+ - Never render mock arrays or fallback mock data in production views.
34
+ - Bind views to real action calls; provide realistic `<Skeleton />` loaders and polite empty states.
35
+ 8. **Forms with `@formisch/react`, Valibot & Shadcn UI**:
36
+ - Define validation schemas using **Valibot** (`* as v from "valibot"`).
37
+ - Manage form state via `@formisch/react`'s `useForm` and bind inputs with `<Form>` and `<Field>` render props alongside Shadcn UI primitives.
38
+ 9. **Mandatory Async Feedback with `toast.promise`**:
39
+ - All async operations and form submissions must be wrapped in **`toast.promise`** (from `sonner`).
40
+ - Define clear `loading`, `success`, and `error` messages. Never rely on manual `toast.success`/`toast.error` branching, and never use browser `alert()`.
41
+ 10. **Code Hygiene & Conventions**:
42
+ - Code, identifiers, and comments strictly in English; UI copy follows target locale.
43
+ - Mandatory `@/...` path aliases and **zero barrel files** (`index.ts`).
44
+ - Use lookup objects (`Record<Key, Value>`) instead of `switch` statements.
45
+ - Use descriptive variable names in callbacks and loops (no single-letter `i`, `e`, `x`).
46
+
47
+ ---
48
+
49
+ ## 2. Domain Directory Taxonomy
50
+
51
+ ```text
52
+ src/
53
+ ├── actions/ # Pure async API layer
54
+ │ └── profile/
55
+ │ ├── get-profile.action.ts # One atomic action (< 100 lines)
56
+ │ └── update-profile.action.ts
57
+ └── pages/ # Or src/features/
58
+ └── profile/
59
+ ├── components/ # Atomic UI components for this view
60
+ │ ├── profile-card.tsx
61
+ │ └── profile-form.tsx
62
+ ├── hooks/ # Page coordinator and state hook
63
+ │ └── use-profile-page.ts
64
+ ├── schemas/ # Valibot schemas for forms/actions
65
+ │ └── profile.schema.ts
66
+ ├── types/ # Domain TypeScript definitions
67
+ │ └── profile.types.ts
68
+ └── profile-page.tsx # Declarative coordinator (< 100 lines)
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 3. End-to-End Implementation Blueprint
74
+
75
+ ### A. Valibot Schema & Domain Action
76
+ ```ts
77
+ // src/pages/profile/schemas/profile.schema.ts
78
+ import * as v from "valibot";
79
+
80
+ export const profileSchema = v.object({
81
+ fullName: v.pipe(v.string(), v.minLength(2, "Name must have at least 2 characters.")),
82
+ email: v.pipe(v.string(), v.email("Please provide a valid email.")),
83
+ });
84
+ export type ProfileFormValues = v.InferOutput<typeof profileSchema>;
85
+
86
+ // src/actions/profile/update-profile.action.ts
87
+ export interface ActionResponse<T> { success: boolean; data?: T; error?: string; }
88
+ export async function updateProfileAction(data: ProfileFormValues): Promise<ActionResponse<ProfileFormValues>> {
89
+ try {
90
+ const res = await fetch("/api/profile", { method: "PUT", body: JSON.stringify(data) });
91
+ return res.ok ? { success: true, data } : { success: false, error: "Unable to save profile." };
92
+ } catch {
93
+ return { success: false, error: "Network error occurred." };
94
+ }
95
+ }
96
+ ```
97
+
98
+ ### B. Domain Custom Hook & Declarative Page
99
+ ```tsx
100
+ // src/pages/profile/hooks/use-profile-page.ts
101
+ import { useState, useTransition } from "react";
102
+ import { toast } from "sonner";
103
+ import { updateProfileAction } from "@/actions/profile/update-profile.action";
104
+ import type { ProfileFormValues } from "@/pages/profile/schemas/profile.schema";
105
+
106
+ export function useProfilePage(initialData: ProfileFormValues) {
107
+ const [profile, setProfile] = useState<ProfileFormValues>(initialData);
108
+
109
+ const handleUpdate = (values: ProfileFormValues): void => {
110
+ toast.promise(
111
+ async () => {
112
+ const res = await updateProfileAction(values);
113
+ if (!res.success || !res.data) {
114
+ throw new Error(res.error ?? "Failed to update profile.");
115
+ }
116
+ setProfile(res.data);
117
+ return res.data;
118
+ },
119
+ {
120
+ loading: "Saving profile changes...",
121
+ success: "Profile updated successfully!",
122
+ error: (err: unknown) => (err instanceof Error ? err.message : "Failed to save profile."),
123
+ }
124
+ );
125
+ };
126
+ return { profile, handleUpdate };
127
+ }
128
+
129
+ // src/pages/profile/profile-page.tsx
130
+ export function ProfilePage({ initialData }: { initialData: ProfileFormValues }) {
131
+ const { profile, handleUpdate } = useProfilePage(initialData);
132
+
133
+ return (
134
+ <main className="container mx-auto max-w-4xl p-6 space-y-6">
135
+ <h1 className="text-2xl font-bold">Profile Settings</h1>
136
+ <ProfileForm defaultValues={profile} onSubmit={handleUpdate} />
137
+ </main>
138
+ );
139
+ }
140
+ ```
141
+
142
+ ### C. Form with `@formisch/react`, Valibot & Shadcn
143
+ ```tsx
144
+ // src/pages/profile/components/profile-form.tsx
145
+ import { useForm, Form, Field } from "@formisch/react";
146
+ import { profileSchema, type ProfileFormValues } from "@/pages/profile/schemas/profile.schema";
147
+ import { Input } from "@/components/ui/input";
148
+ import { Label } from "@/components/ui/label";
149
+ import { Button } from "@/components/ui/button";
150
+
151
+ interface ProfileFormProps {
152
+ defaultValues: ProfileFormValues;
153
+ onSubmit: (values: ProfileFormValues) => void;
154
+ }
155
+
156
+ export function ProfileForm({ defaultValues, onSubmit }: ProfileFormProps) {
157
+ const [form, dispatch] = useForm({ schema: profileSchema, initialValues: defaultValues, onSubmit });
158
+
159
+ return (
160
+ <Form form={form} dispatch={dispatch} className="space-y-4 rounded-lg border p-6">
161
+ <Field form={form} name="fullName">
162
+ {(field) => (
163
+ <div className="space-y-1.5">
164
+ <Label htmlFor={field.props.id}>Full Name</Label>
165
+ <Input {...field.props} placeholder="Jane Doe" />
166
+ {field.errors[0] && <p className="text-sm text-destructive">{field.errors[0]}</p>}
167
+ </div>
168
+ )}
169
+ </Field>
170
+ <Button type="submit">Save Profile</Button>
171
+ </Form>
172
+ );
173
+ }
174
+ ```
175
+
176
+ ---
177
+
178
+ ## 4. Anti-Patterns to Avoid
179
+
180
+ | Anti-Pattern | Why It Fails | Correct Pattern |
181
+ | :--- | :--- | :--- |
182
+ | Inline `fetch` / Axios in hooks or components | Couples UI to transport layer; hard to test/mock | Delegate network requests to `src/actions/<domain>/<action>.action.ts` |
183
+ | State/handlers directly in `page.tsx` | Bloats coordinator, breaks SRP, complicates re-renders | Encapsulate in `use<Domain>Page` custom hook |
184
+ | Monolithic view file (> 200 lines) | Hard to navigate, maintain, and review | Atomize into `components/` subcomponents (< 200 lines each) |
185
+ | Hardcoded mock arrays in views | Creates false assumptions and leaks mocks to production | Use real action bindings, `<Skeleton />`, and polite empty states |
186
+ | Manual toasts or native `alert()` | Inconsistent async feedback and poor UX | Wrap all async mutations strictly in `toast.promise` |
187
+ | Re-exporting via `index.ts` barrel files | Defeats tree-shaking and causes circular dependency traps | Import directly using `@/` path aliases |
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/domains/detector/detect-agents.ts","../src/domains/detector/detect-dependencies.ts","../src/domains/registry/skill-catalog.ts","../src/domains/detector/project-detector.ts","../src/domains/ui/ui-orchestrator.ts","../src/domains/ui/banners.ts","../src/domains/ui/prompt-skills.ts","../src/domains/ui/prompt-agents.ts","../src/domains/ui/prompt-confirm-overwrite.ts","../src/domains/installer/verify-skill-exists.ts","../src/domains/installer/install-orchestrator.ts","../src/domains/adapters/antigravity.adapter.ts","../src/domains/adapters/claude.adapter.ts","../src/domains/adapters/cursor.adapter.ts","../src/domains/adapters/gemini.adapter.ts","../src/domains/installer/resolve-skills-source.ts","../src/main.ts"],"sourcesContent":["import { readdirSync } from \"node:fs\";\nimport type { AgentId } from \"@/types/agent.types\";\n\nexport function detectAgents(targetPath: string): AgentId[] {\n const detected: AgentId[] = [];\n\n try {\n const entries = new Set(readdirSync(targetPath));\n\n if (entries.has(\".agents\")) {\n detected.push(\"antigravity\");\n }\n if (entries.has(\".claude\")) {\n detected.push(\"claude\");\n }\n if (entries.has(\".cursor\")) {\n detected.push(\"cursor\");\n }\n if (entries.has(\".gemini\")) {\n detected.push(\"gemini\");\n }\n } catch {\n // If directory cannot be read, return empty list gracefully\n }\n\n return detected;\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface PackageJsonDetection {\n dependencies: string[];\n exists: boolean;\n}\n\nexport function detectDependencies(targetPath: string): PackageJsonDetection {\n const packageJsonPath = join(targetPath, \"package.json\");\n\n if (!existsSync(packageJsonPath)) {\n return { dependencies: [], exists: false };\n }\n\n try {\n const raw = readFileSync(packageJsonPath, \"utf-8\");\n const parsed = JSON.parse(raw) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n const direct = Object.keys(parsed.dependencies ?? {});\n const dev = Object.keys(parsed.devDependencies ?? {});\n const all = Array.from(new Set([...direct, ...dev]));\n\n return { dependencies: all, exists: true };\n } catch {\n return { dependencies: [], exists: true };\n }\n}\n","import type { SkillMetadata } from \"@/types/skill.types\";\n\nexport const SKILL_CATALOG: SkillMetadata[] = [\n {\n id: \"best-practices\",\n name: \"Universal Best Practices\",\n description: \"Multi-language clean code standards, early returns, no barrel files, strict typing, <200 lines\",\n dependencies: [],\n directoryName: \"best-practices\",\n },\n {\n id: \"hono\",\n name: \"Hono Architecture (Valibot)\",\n description: \"Enterprise Hono API standard with Valibot validation, isolated Actions layer, and RPC bindings\",\n dependencies: [\"hono\", \"@hono/valibot-validator\", \"@hono/standard-validator\"],\n directoryName: \"hono\",\n },\n];\n","import { detectAgents } from \"@/domains/detector/detect-agents\";\nimport { detectDependencies } from \"@/domains/detector/detect-dependencies\";\nimport { SKILL_CATALOG } from \"@/domains/registry/skill-catalog\";\nimport type { ProjectContext } from \"@/types/context.types\";\n\nexport function detectProjectContext(targetPath: string): ProjectContext {\n const detectedAgents = detectAgents(targetPath);\n const { dependencies, exists } = detectDependencies(targetPath);\n\n const suggestedSkillIds: string[] = [\"best-practices\"];\n\n for (const skill of SKILL_CATALOG) {\n if (skill.dependencies.length === 0) {\n continue;\n }\n const hasMatch = skill.dependencies.some((dep) => dependencies.includes(dep));\n if (hasMatch && !suggestedSkillIds.includes(skill.id)) {\n suggestedSkillIds.push(skill.id);\n }\n }\n\n return {\n targetPath,\n detectedDependencies: dependencies,\n suggestedSkillIds,\n detectedAgents,\n packageJsonExists: exists,\n };\n}\n","import * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { renderBanner } from \"@/domains/ui/banners\";\nimport { promptSkills } from \"@/domains/ui/prompt-skills\";\nimport { promptAgents } from \"@/domains/ui/prompt-agents\";\nimport { promptConfirmOverwrite } from \"@/domains/ui/prompt-confirm-overwrite\";\nimport { verifySkillExists } from \"@/domains/installer/verify-skill-exists\";\nimport { installSkills } from \"@/domains/installer/install-orchestrator\";\nimport { SKILL_CATALOG } from \"@/domains/registry/skill-catalog\";\nimport type { ProjectContext } from \"@/types/context.types\";\nimport type { AgentId } from \"@/types/agent.types\";\n\nexport async function runInteractiveUi(context: ProjectContext): Promise<void> {\n console.log(renderBanner());\n p.intro(pc.magenta(\"Welcome to GrooveSkills! Let's tailor your AI agents with clean architectural standards.\"));\n\n if (context.detectedDependencies.length > 0) {\n p.note(\n `Detected dependencies: ${context.detectedDependencies.map((d) => pc.bold(d)).join(\", \")}\\n` +\n `Active AI folders: ${context.detectedAgents.map((a) => pc.green(a)).join(\", \") || \"None (will be created)\"}`,\n \"Project Environment\"\n );\n }\n\n const selectedSkills = await promptSkills(context.suggestedSkillIds);\n if (p.isCancel(selectedSkills)) {\n p.cancel(\"Installation cancelled. Have a wonderful day!\");\n process.exit(0);\n }\n\n const selectedAgents = await promptAgents(context.detectedAgents);\n if (p.isCancel(selectedAgents)) {\n p.cancel(\"Installation cancelled. Have a wonderful day!\");\n process.exit(0);\n }\n\n let forceOverwrite = true;\n const anyExists = (selectedSkills as string[]).some((skillId) => {\n const meta = SKILL_CATALOG.find((s) => s.id === skillId);\n return meta ? verifySkillExists(context.targetPath, meta.directoryName, selectedAgents as AgentId[]) : false;\n });\n\n if (anyExists) {\n const confirm = await promptConfirmOverwrite();\n if (p.isCancel(confirm) || !confirm) {\n p.cancel(\"Installation stopped to protect existing skills.\");\n process.exit(0);\n }\n forceOverwrite = Boolean(confirm);\n }\n\n const s = p.spinner();\n s.start(\"Installing and adapting skills across your configured AI agents...\");\n\n const result = await installSkills({\n targetPath: context.targetPath,\n selectedSkillIds: selectedSkills as string[],\n selectedAgents: selectedAgents as AgentId[],\n forceOverwrite,\n });\n\n s.stop(pc.green(\"All skills have been successfully installed and configured!\"));\n\n p.note(\n `Configured skills: ${result.installedSkills.map((s) => pc.bold(s)).join(\", \")}\\n` +\n `Target agents: ${result.configuredAgents.map((a) => pc.cyan(a)).join(\", \")}`,\n \"Installation Summary\"\n );\n\n p.outro(pc.green(\"Your AI programming pair is now empowered with your team's architectural habits! 🚀\"));\n}\n","import pc from \"picocolors\";\n\nexport function renderBanner(): string {\n const title = pc.bold(pc.white(\" GROOVE SKILLS \"));\n const badge = pc.bgMagenta(pc.black(title));\n const subtitle = pc.dim(\"Multi-Agent AI Skills & Architectural Standards\");\n\n return `${badge} ${subtitle}\\n`;\n}\n","import * as p from \"@clack/prompts\";\nimport { SKILL_CATALOG } from \"@/domains/registry/skill-catalog\";\n\nexport async function promptSkills(suggestedSkillIds: string[]): Promise<string[] | symbol> {\n const options = SKILL_CATALOG.map((skill) => {\n const isSuggested = suggestedSkillIds.includes(skill.id);\n return {\n value: skill.id,\n label: skill.name,\n hint: isSuggested ? \"Suggested based on your project stack\" : skill.description,\n };\n });\n\n const selected = await p.multiselect({\n message: \"Which skills would you like to install in this project?\",\n options,\n initialValues: suggestedSkillIds,\n required: true,\n });\n\n return selected;\n}\n","import * as p from \"@clack/prompts\";\nimport type { AgentId } from \"@/types/agent.types\";\n\nexport async function promptAgents(detectedAgents: AgentId[]): Promise<AgentId[] | symbol> {\n const options: Array<{ value: AgentId; label: string; hint: string }> = [\n { value: \"antigravity\", label: \"Antigravity\", hint: \".agents/skills (Google Agentic IDE)\" },\n { value: \"claude\", label: \"Claude Code\", hint: \".claude/skills (Anthropic Claude Code)\" },\n { value: \"cursor\", label: \"Cursor\", hint: \".cursor/rules/*.mdc (Cursor IDE Rules)\" },\n { value: \"gemini\", label: \"Gemini Code Assist\", hint: \".gemini/skills (Google Gemini)\" },\n ];\n\n const initialValues: AgentId[] = detectedAgents.length > 0 ? detectedAgents : [\"antigravity\"];\n\n const selected = await p.multiselect({\n message: \"Which AI agents should be configured?\",\n options,\n initialValues,\n required: true,\n });\n\n return selected as AgentId[] | symbol;\n}\n","import * as p from \"@clack/prompts\";\n\nexport async function promptConfirmOverwrite(): Promise<boolean | symbol> {\n return await p.confirm({\n message: \"Some skills already exist in this project. Would you like to overwrite them with the latest version?\",\n initialValue: true,\n });\n}\n","import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AgentId } from \"@/types/agent.types\";\n\nconst AGENT_DESTINATION_MAP: Record<AgentId, (dir: string) => string> = {\n antigravity: (dir) => join(\".agents\", \"skills\", dir),\n claude: (dir) => join(\".claude\", \"skills\", dir),\n cursor: (dir) => join(\".cursor\", \"rules\", `${dir}.mdc`),\n gemini: (dir) => join(\".gemini\", \"skills\", dir),\n};\n\nexport function verifySkillExists(\n targetPath: string,\n skillDirectoryName: string,\n agents: AgentId[]\n): boolean {\n for (const agent of agents) {\n const relativePathResolver = AGENT_DESTINATION_MAP[agent];\n const destination = join(targetPath, relativePathResolver(skillDirectoryName));\n if (existsSync(destination)) {\n return true;\n }\n }\n\n return false;\n}\n","import { join } from \"node:path\";\nimport { AntigravityAdapter } from \"@/domains/adapters/antigravity.adapter\";\nimport { ClaudeAdapter } from \"@/domains/adapters/claude.adapter\";\nimport { CursorAdapter } from \"@/domains/adapters/cursor.adapter\";\nimport { GeminiAdapter } from \"@/domains/adapters/gemini.adapter\";\nimport { resolveSkillsSourceRoot } from \"@/domains/installer/resolve-skills-source\";\nimport { SKILL_CATALOG } from \"@/domains/registry/skill-catalog\";\nimport type { AgentAdapter } from \"@/domains/adapters/adapter.interface\";\nimport type { InstallOptions, InstallResult } from \"@/types/installer.types\";\nimport type { AgentId } from \"@/types/agent.types\";\n\nconst ADAPTER_REGISTRY: Record<AgentId, AgentAdapter> = {\n antigravity: new AntigravityAdapter(),\n claude: new ClaudeAdapter(),\n cursor: new CursorAdapter(),\n gemini: new GeminiAdapter(),\n};\n\nexport async function installSkills(options: InstallOptions): Promise<InstallResult> {\n const sourceRoot = resolveSkillsSourceRoot();\n const createdFiles: string[] = [];\n\n for (const skillId of options.selectedSkillIds) {\n const skill = SKILL_CATALOG.find((item) => item.id === skillId);\n if (!skill) {\n continue;\n }\n\n const skillSourceDir = join(sourceRoot, skill.directoryName);\n\n for (const agentId of options.selectedAgents) {\n const adapter = ADAPTER_REGISTRY[agentId];\n if (!adapter) {\n continue;\n }\n\n const files = await adapter.installSkill(options.targetPath, skill, skillSourceDir);\n createdFiles.push(...files);\n }\n }\n\n return {\n installedSkills: options.selectedSkillIds,\n configuredAgents: options.selectedAgents,\n createdFiles,\n };\n}\n","import { cpSync, mkdirSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AgentAdapter } from \"@/domains/adapters/adapter.interface\";\nimport type { SkillMetadata } from \"@/types/skill.types\";\n\nexport class AntigravityAdapter implements AgentAdapter {\n readonly id = \"antigravity\" as const;\n readonly name = \"Antigravity\";\n readonly targetRelativeDir = \".agents/skills\";\n\n async installSkill(\n targetPath: string,\n skill: SkillMetadata,\n skillSourceDir: string\n ): Promise<string[]> {\n const destination = join(targetPath, this.targetRelativeDir, skill.directoryName);\n\n if (!existsSync(destination)) {\n mkdirSync(destination, { recursive: true });\n }\n\n cpSync(skillSourceDir, destination, { recursive: true });\n\n return [destination];\n }\n}\n","import { cpSync, mkdirSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AgentAdapter } from \"@/domains/adapters/adapter.interface\";\nimport type { SkillMetadata } from \"@/types/skill.types\";\n\nexport class ClaudeAdapter implements AgentAdapter {\n readonly id = \"claude\" as const;\n readonly name = \"Claude Code\";\n readonly targetRelativeDir = \".claude/skills\";\n\n async installSkill(\n targetPath: string,\n skill: SkillMetadata,\n skillSourceDir: string\n ): Promise<string[]> {\n const destination = join(targetPath, this.targetRelativeDir, skill.directoryName);\n\n if (!existsSync(destination)) {\n mkdirSync(destination, { recursive: true });\n }\n\n cpSync(skillSourceDir, destination, { recursive: true });\n\n return [destination];\n }\n}\n","import { writeFileSync, mkdirSync, existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AgentAdapter } from \"@/domains/adapters/adapter.interface\";\nimport type { SkillMetadata } from \"@/types/skill.types\";\n\nexport class CursorAdapter implements AgentAdapter {\n readonly id = \"cursor\" as const;\n readonly name = \"Cursor\";\n readonly targetRelativeDir = \".cursor/rules\";\n\n async installSkill(\n targetPath: string,\n skill: SkillMetadata,\n skillSourceDir: string\n ): Promise<string[]> {\n const rulesDir = join(targetPath, this.targetRelativeDir);\n\n if (!existsSync(rulesDir)) {\n mkdirSync(rulesDir, { recursive: true });\n }\n\n const skillMdPath = join(skillSourceDir, \"SKILL.md\");\n let content = \"\";\n\n if (existsSync(skillMdPath)) {\n content = readFileSync(skillMdPath, \"utf-8\");\n }\n\n const mdcHeader = `---\ndescription: ${skill.description}\nglobs: [\"**/*\"]\nalwaysApply: false\n---\n\n`;\n\n const cleanedBody = content.replace(/^---[\\s\\S]*?---\\s*/, \"\");\n const targetFile = join(rulesDir, `${skill.id}.mdc`);\n\n writeFileSync(targetFile, `${mdcHeader}${cleanedBody}`, \"utf-8\");\n\n return [targetFile];\n }\n}\n","import { cpSync, mkdirSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AgentAdapter } from \"@/domains/adapters/adapter.interface\";\nimport type { SkillMetadata } from \"@/types/skill.types\";\n\nexport class GeminiAdapter implements AgentAdapter {\n readonly id = \"gemini\" as const;\n readonly name = \"Gemini Code Assist\";\n readonly targetRelativeDir = \".gemini/skills\";\n\n async installSkill(\n targetPath: string,\n skill: SkillMetadata,\n skillSourceDir: string\n ): Promise<string[]> {\n const destination = join(targetPath, this.targetRelativeDir, skill.directoryName);\n\n if (!existsSync(destination)) {\n mkdirSync(destination, { recursive: true });\n }\n\n cpSync(skillSourceDir, destination, { recursive: true });\n\n return [destination];\n }\n}\n","import { fileURLToPath } from \"node:url\";\nimport { dirname, resolve, join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\nexport function resolveSkillsSourceRoot(): string {\n const currentFilePath = fileURLToPath(import.meta.url);\n const currentDir = dirname(currentFilePath);\n\n const candidate1 = resolve(currentDir, \"../../../skills\");\n if (existsSync(candidate1)) {\n return candidate1;\n }\n\n const candidate2 = resolve(currentDir, \"../../skills\");\n if (existsSync(candidate2)) {\n return candidate2;\n }\n\n const candidate3 = resolve(process.cwd(), \"skills\");\n if (existsSync(candidate3)) {\n return candidate3;\n }\n\n return join(process.cwd(), \"skills\");\n}\n","import { detectProjectContext } from \"@/domains/detector/project-detector\";\nimport { runInteractiveUi } from \"@/domains/ui/ui-orchestrator\";\nimport { installSkills } from \"@/domains/installer/install-orchestrator\";\nimport type { AgentId } from \"@/types/agent.types\";\n\nexport interface CliOptions {\n skills?: string[];\n agents?: AgentId[];\n yes?: boolean;\n}\n\nexport async function runGrooveSkills(options: CliOptions = {}): Promise<void> {\n const targetPath = process.cwd();\n const context = detectProjectContext(targetPath);\n\n if (options.skills && options.skills.length > 0 && options.agents && options.agents.length > 0) {\n await installSkills({\n targetPath,\n selectedSkillIds: options.skills,\n selectedAgents: options.agents,\n forceOverwrite: Boolean(options.yes),\n });\n return;\n }\n\n await runInteractiveUi(context);\n}\n"],"mappings":"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","names":["p","pc","p","p","existsSync","join","join","existsSync","join","cpSync","mkdirSync","existsSync","join","mkdirSync","existsSync","readFileSync","join","cpSync","mkdirSync","existsSync","join","join","existsSync","join","pc","s","confirm"]}