@hiver/skills 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +394 -0
  2. package/build.js +15 -0
  3. package/collections/common/skills/create-prd/SKILL.md +43 -0
  4. package/collections/common/skills/discuss-problem/SKILL.md +55 -0
  5. package/collections/common/skills/explore-codebase/SKILL.md +79 -0
  6. package/collections/common/skills/prd-to-milestone/SKILL.md +40 -0
  7. package/collections/common/skills/refractor-and-clean/SKILL.md +41 -0
  8. package/collections/common/skills/solve-issue/SKILL.md +31 -0
  9. package/collections/common/skills/write-test-cases/SKILL.md +93 -0
  10. package/collections/web/agents/build-feature.md +42 -0
  11. package/collections/web/agents/build-milestone.md +61 -0
  12. package/collections/web/skills/build-component/SKILL.md +66 -0
  13. package/collections/web/skills/build-component/palette/README.md +3 -0
  14. package/collections/web/skills/build-component/palette/colors.md +87 -0
  15. package/collections/web/skills/build-component/references/best-practices.md +8 -0
  16. package/collections/web/skills/build-component/references/component-organization.md +21 -0
  17. package/collections/web/skills/build-component/references/figma-integration.md +5 -0
  18. package/collections/web/skills/build-component/references/icons.md +8 -0
  19. package/collections/web/skills/build-component/references/layout-and-structure.md +8 -0
  20. package/collections/web/skills/build-component/references/state-management.md +25 -0
  21. package/collections/web/skills/build-component/references/ui-kit-and-styling.md +13 -0
  22. package/collections/web/skills/build-component/typography/README.md +3 -0
  23. package/collections/web/skills/build-component/typography/variants.md +79 -0
  24. package/collections/web/skills/connect-api/SKILL.md +23 -0
  25. package/collections/web/skills/connect-api/references/api-call-structure.md +84 -0
  26. package/collections/web/skills/connect-api/references/best-practices.md +10 -0
  27. package/collections/web/skills/connect-api/references/data-fetchers.md +52 -0
  28. package/collections/web/skills/connect-api/references/error-handling.md +34 -0
  29. package/collections/web/skills/connect-api/references/hook-organization.md +11 -0
  30. package/collections/web/skills/connect-api/references/query-keys.md +21 -0
  31. package/collections/web/skills/connect-api/references/react-query-usage.md +83 -0
  32. package/collections/web/skills/connect-api/references/url-placeholders.md +17 -0
  33. package/collections/web/skills/scaffold-react-feature/SKILL.md +208 -0
  34. package/dist/cli.js +990 -0
  35. package/package.json +33 -0
@@ -0,0 +1,84 @@
1
+ # API Call Structure
2
+
3
+ ## For Modern Auth Fetchers (HiverNewAuthCall based)
4
+
5
+ - **Use async/await pattern** with error handling that returns `[data, null]` or `[null, error]`:
6
+ - Applies to: `AiBifrostAuth`, `CustomFieldsService`, `AutomationService`, `HiverSlaNewAuth`, `CsatNewAuthCall`, `TagBifrostAuth`, `HiverIntegrationBifrostAuth`, `HiverChatBifrostAuth`, `HiverNotesAxios`, `OutOfOfficeService`, `CustomerPortalApiService`, `HiverNotifCall`
7
+ ```javascript
8
+ import { AiBifrostAuth } from "../../../../utils/AiNewAuth";
9
+ // OR any other modern auth fetcher
10
+
11
+ const fetchData = async (param) => {
12
+ try {
13
+ const response = await AiBifrostAuth({
14
+ url: `/v1/endpoint?param=${param}`,
15
+ method: "GET", // or "POST", "PUT", "PATCH", "DELETE"
16
+ data: {}, // For POST/PUT/PATCH
17
+ });
18
+ return [response.data, null];
19
+ } catch (err) {
20
+ return [null, err.message];
21
+ }
22
+ };
23
+ ```
24
+
25
+ ## For createAxios based Fetchers
26
+
27
+ - **Use async/await pattern** with error handling:
28
+ - Applies to: `AiAxios`, `HiverCustomerPortalAxios`
29
+ ```javascript
30
+ import { AiAxios } from "../../../../utils/AiAxios";
31
+
32
+ const fetchData = async (param) => {
33
+ try {
34
+ const response = await AiAxios({
35
+ url: `/v1/endpoint?param=${param}`,
36
+ method: "GET", // or "POST", "PUT", "PATCH", "DELETE"
37
+ data: {}, // For POST/PUT/PATCH
38
+ });
39
+ return [response.data, null];
40
+ } catch (err) {
41
+ return [null, err.message];
42
+ }
43
+ };
44
+ ```
45
+
46
+ ## For Legacy HiverAxios
47
+
48
+ - **Use async/await with try/catch**:
49
+ ```javascript
50
+ import { HiverAxios } from "h-extension-common";
51
+
52
+ const fetchData = async (payload) => {
53
+ try {
54
+ const response = await HiverAxios({
55
+ url: `/api/endpoint`,
56
+ type: "GET", // or "POST", "PUT", "DELETE" - Note: uses "type" not "method"
57
+ params: payload, // Request body/params
58
+ });
59
+ return [response.data, null];
60
+ } catch (err) {
61
+ return [null, err.message];
62
+ }
63
+ };
64
+ ```
65
+
66
+ ## For HiverAnalyticsAxios
67
+
68
+ - **Use async/await with try/catch**:
69
+ ```javascript
70
+ import { HiverAnalyticsAxios } from "../../../../utils/HiverAnalyticsAxios";
71
+
72
+ const fetchData = async (payload) => {
73
+ try {
74
+ const response = await HiverAnalyticsAxios({
75
+ url: `/api/endpoint`,
76
+ type: "GET", // or "POST", "PUT", "PATCH", "DELETE"
77
+ params: payload, // Request body/params
78
+ });
79
+ return [response.parsedBody, null]; // Note: HiverAnalyticsAxios returns parsedBody
80
+ } catch (err) {
81
+ return [null, err.message];
82
+ }
83
+ };
84
+ ```
@@ -0,0 +1,10 @@
1
+ # Best Practices
2
+
3
+ - **Keep API functions pure** - separate API logic from React Query hooks
4
+ - **Use TypeScript types** if the project uses TypeScript
5
+ - **Add JSDoc comments** for complex hooks explaining parameters and return values
6
+ - **Handle loading states** using `isLoading`, `isFetching` from React Query
7
+ - **Handle error states** using `isError`, `error` from React Query
8
+ - **Use `enabled` option** for conditional queries
9
+ - **Use `refetchOnMount`, `refetchOnWindowFocus`** options when needed
10
+ - **Export query keys** from hook files for use in other components
@@ -0,0 +1,52 @@
1
+ # Data Fetcher Selection
2
+
3
+ - **All data fetcher utilities are located in `react-extn/src/utils`**
4
+ - **Use the appropriate fetcher** based on the service you're working with:
5
+
6
+ ## Modern Auth Fetchers (HiverNewAuthCall based)
7
+
8
+ These use `method` (GET, POST, PUT, PATCH, DELETE) and return promises:
9
+
10
+ | Fetcher | Use Case | Service | Import Path |
11
+ |---------|----------|---------|-------------|
12
+ | `AiBifrostAuth` | AI service endpoints | AI Service | `../../../../utils/AiNewAuth` |
13
+ | `CustomFieldsService` | Custom fields operations | Custom Fields Service | `../../../../utils/CustomFieldsService` |
14
+ | `AutomationService` | Automation workflows | Automations Service | `../../../../utils/AutomationService` |
15
+ | `HiverSlaNewAuth` | SLA management | SLA Service | `../../../../utils/slaNewAuth` |
16
+ | `CsatNewAuthCall` | CSAT surveys | CSAT Service | `../../../../utils/csatNewAuth` |
17
+ | `TagBifrostAuth` | Tag operations | Tags Service | `../../../../utils/tagNewAuth` |
18
+ | `HiverIntegrationBifrostAuth` | Integration endpoints | Integration Service | `../../../../utils/integrationNewAuth` |
19
+ | `HiverChatBifrostAuth` | Chat/channels operations | Channels Service | `../../../../utils/chatNewAuth` |
20
+ | `HiverNotesAxios` | Notes operations | Notes Service | `../../../../utils/HiverNotesNewAuth` |
21
+ | `OutOfOfficeService` | Schedule management | Schedule Service | `../../../../utils/OutOfOfficeService` |
22
+ | `CustomerPortalApiService` | Customer portal | Customer Portal Service | `../../../../utils/CustomerPortalApiService` |
23
+ | `HiverNotifCall` | Notifications | Notifications Service | `../../../../utils/notificationAuthCall` |
24
+
25
+ **Usage pattern for modern auth fetchers:**
26
+ ```javascript
27
+ import { AiBifrostAuth } from "../../../../utils/AiNewAuth";
28
+ // OR
29
+ import { CustomFieldsService } from "../../../../utils/CustomFieldsService";
30
+
31
+ const response = await AiBifrostAuth({
32
+ url: `/v1/endpoint`,
33
+ method: "GET", // or "POST", "PUT", "PATCH", "DELETE"
34
+ data: {}, // For POST/PUT/PATCH
35
+ });
36
+ ```
37
+
38
+ ## createAxios based Fetchers
39
+
40
+ These use `method` and return promises:
41
+
42
+ | Fetcher | Use Case | Service | Import Path |
43
+ |---------|----------|---------|-------------|
44
+ | `AiAxios` | AI service (legacy) | AI Service | `../../../../utils/AiAxios` |
45
+ | `HiverCustomerPortalAxios` | Customer portal (legacy) | Customer Portal Service | `../../../../utils/CustomerPortalAxios` |
46
+
47
+ ## Legacy/Custom Fetchers
48
+
49
+ | Fetcher | Use Case | Service | Import Path |
50
+ |---------|----------|---------|-------------|
51
+ | `HiverAxios` | Legacy API calls | Default Hiver API | `h-extension-common` |
52
+ | `HiverAnalyticsAxios` | Analytics data | Analytics Service | `../../../../utils/HiverAnalyticsAxios` |
@@ -0,0 +1,34 @@
1
+ # Error Handling
2
+
3
+ - **Handle errors in mutation hooks**:
4
+ ```javascript
5
+ import { useSnackbar } from "../../../../common";
6
+ import { theme } from "@hiver/hiver-ui-kit";
7
+
8
+ const showSnackbar = useSnackbar();
9
+
10
+ // In mutation onSuccess handler
11
+ onSuccess: (response) => {
12
+ const [result, error] = response;
13
+ if (error) {
14
+ showSnackbar({
15
+ message: error || "Operation failed",
16
+ background: theme.palette.red.primary,
17
+ });
18
+ return;
19
+ }
20
+ // Handle success...
21
+ },
22
+
23
+ // In mutation onError handler (for unexpected errors)
24
+ onError: (error, variables, context) => {
25
+ showSnackbar({
26
+ message: error.message || "Operation failed",
27
+ background: theme.palette.red.primary,
28
+ });
29
+ // Rollback optimistic updates if needed
30
+ if (context?.previousData) {
31
+ queryClient.setQueryData([API_QUERIES.resource], context.previousData);
32
+ }
33
+ }
34
+ ```
@@ -0,0 +1,11 @@
1
+ # Hook Organization
2
+
3
+ - **Always create separate custom hooks** for querying and mutating server data
4
+ - **Place hooks in a `hooks` folder** within the module/component directory
5
+ - Example structure: `react-extn/src/AiCopilot/Pages/Chat/hooks/` or `react-extn/src/AiCopilot/Pages/Tags/hooks/`
6
+ - **Naming convention**:
7
+ - Query hooks: `useGet[ResourceName].js` (e.g., `useGetChatList.js`, `useGetSuggestedTags.js`)
8
+ - Mutation hooks: `use[Action][ResourceName].js` (e.g., `useSendChatMessage.js`, `useApplyTags.js`)
9
+ - **Reference examples**:
10
+ - `react-extn/src/AiCopilot/Pages/Chat/hooks/useGetChatList.js`
11
+ - `react-extn/src/AiCopilot/Pages/Tags/hooks/useGetSuggestedTags.js`
@@ -0,0 +1,21 @@
1
+ # Query Key and Mutation Key Management
2
+
3
+ - **Never hardcode query/mutation keys** directly in hook files
4
+ - **Always define keys in a constants file** for the module
5
+ - Create or use existing `constants/queries.js` file in the module directory
6
+ - Example: `react-extn/src/AiCopilot/Pages/Chat/constants/queries.js` or `react-extn/src/AiCopilot/Pages/Tags/constants/queries.js`
7
+ - **Naming convention**: Use descriptive constant names in an `API_QUERIES` object
8
+ ```javascript
9
+ // In constants/queries.js file
10
+ export const API_QUERIES = {
11
+ list: 'ai_chat_list',
12
+ send: 'ai_chat_message',
13
+ tagsList: 'ai_tags_list',
14
+ };
15
+ ```
16
+ - **Include dynamic parameters** in query keys for proper cache invalidation:
17
+ ```javascript
18
+ queryKey: [API_QUERIES.list, smIdsKey, conversationId]
19
+ // OR for single parameter
20
+ queryKey: [API_QUERIES.tagsList, String(smId), String(conversationId)]
21
+ ```
@@ -0,0 +1,83 @@
1
+ # React Query Usage
2
+
3
+ ## For Data Fetching (Queries)
4
+
5
+ - **Use `useQuery` from `@tanstack/react-query`** for GET requests
6
+ - **Structure**:
7
+ ```javascript
8
+ import { useQuery } from "@tanstack/react-query";
9
+ import { AiBifrostAuth } from "../../../../utils/AiNewAuth"; // For AI service
10
+ // OR
11
+ import { HiverAxios } from "h-extension-common"; // For legacy API calls
12
+
13
+ // Import query keys from constants (see section 3)
14
+ import { API_QUERIES } from '../constants/queries';
15
+
16
+ // API function - returns [data, null] on success or [null, error] on failure
17
+ const fetchResource = async (param1, param2) => {
18
+ const response = await AiBifrostAuth({
19
+ url: `/v1/resource?param=${param1}`,
20
+ method: "GET",
21
+ });
22
+ return response.data;
23
+ };
24
+
25
+ // Custom hook
26
+ export const useGetResource = ({ param1, param2, enabled = true }) => {
27
+ return useQuery({
28
+ queryKey: [API_QUERIES.resource, param1, param2],
29
+ queryFn: () => fetchResource(param1, param2),
30
+ enabled: enabled && param1 !== undefined,
31
+ refetchOnMount: true, // Optional
32
+ });
33
+ };
34
+ ```
35
+
36
+ ## For Data Mutations
37
+
38
+ - **Use `useMutation` from `@tanstack/react-query`** for POST, PUT, PATCH, DELETE requests
39
+ - **Structure**:
40
+ ```javascript
41
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
42
+ import { AiBifrostAuth } from "../../../../utils/AiNewAuth"; // For AI service
43
+ // OR
44
+ import { HiverAxios } from "h-extension-common"; // For legacy API calls
45
+ import { useSnackbar } from "../../../../common";
46
+ import { theme } from "@hiver/hiver-ui-kit";
47
+ import { API_QUERIES } from '../constants/queries';
48
+
49
+ // API function - returns [data, null] on success or [null, error] on failure
50
+ const updateResource = async (id, data) => {
51
+
52
+ const response = await AiBifrostAuth({
53
+ url: `/v1/resource/${id}`,
54
+ method: "POST",
55
+ data: data,
56
+ });
57
+ return response.data
58
+
59
+ };
60
+
61
+ // Custom hook
62
+ export const useUpdateResource = () => {
63
+ const queryClient = useQueryClient();
64
+ const showSnackbar = useSnackbar();
65
+
66
+ return useMutation({
67
+ mutationFn: ({ id, data }) => updateResource(id, data),
68
+ onSuccess: (response) => {
69
+ // Invalidate related queries
70
+ queryClient.invalidateQueries({ queryKey: [API_QUERIES.resource] });
71
+ showSnackbar({
72
+ message: "Resource updated successfully",
73
+ });
74
+ },
75
+ onError: () => {
76
+ showSnackbar({
77
+ message: "Failed to update resource",
78
+ background: theme.palette.red.primary,
79
+ });
80
+ },
81
+ });
82
+ };
83
+ ```
@@ -0,0 +1,17 @@
1
+ # URL and Method Placeholders
2
+
3
+ - **Keep placeholders** for URL and HTTP method when creating hooks
4
+ - **Format**: Use clear comments indicating where to fill in:
5
+ ```javascript
6
+ const fetchData = async (id) => {
7
+ try {
8
+ const response = await AiBifrostAuth({
9
+ url: `/v1/resource/${id}`, // TODO: Update endpoint URL
10
+ method: "GET", // TODO: Update HTTP method (GET, POST, PUT, PATCH, DELETE)
11
+ });
12
+ return [response.data, null];
13
+ } catch (err) {
14
+ return [null, err.message];
15
+ }
16
+ };
17
+ ```
@@ -0,0 +1,208 @@
1
+ ---
2
+ name: scaffold-react-feature
3
+ description: Generate standard feature folder structure and boilerplate for new React features/modules. Internal skill — used by build-milestone agent, not invoked directly by users.
4
+ ---
5
+
6
+ # Scaffold Feature
7
+
8
+ **Trigger:** Only when building a **new feature or module**. Do NOT use for bug fixes.
9
+
10
+ **Used by:** `build-milestone` agent internally. Not invoked directly by users.
11
+
12
+ ---
13
+
14
+ ## Step 1 — Determine Structure Needs
15
+
16
+ Before creating anything, infer from the milestone/context:
17
+
18
+ 1. **Does the module have multiple distinct views?**
19
+ - Yes → use `pages/` folder + internal routing in `index.js`
20
+ - No → write module logic directly in `index.js`
21
+
22
+ 2. **How is the module consumed?** (this is the consumer's concern, not the scaffold's)
23
+ - As a route component somewhere in the app
24
+ - Directly imported inside another component
25
+ - Either way, the internal structure is the same
26
+
27
+ ---
28
+
29
+ ## Step 2 — Folder Structure
30
+
31
+ **Only create a directory if there is real content for it.** The list below is the ceiling, not the floor. A small feature may only need `index.js`, `constants.js`, and a hook file.
32
+
33
+ ```
34
+ <FeatureName>/
35
+ index.js ← entry point (routing + providers, OR direct module logic if no pages)
36
+ constants.js ← query keys, route paths, enums, UI strings
37
+ utils.js ← pure helper functions (only if needed)
38
+ components/ ← shared UI components reused across pages (only if needed)
39
+ hooks/ ← API hooks and custom hooks (only if needed)
40
+ context/ ← React context + useReducer (only if local state is too complex for useState)
41
+ pages/ ← sub-page components (only if module has multiple distinct views)
42
+ ```
43
+
44
+ Each page inside `pages/` is an independent mini-module and can recursively follow the same structure.
45
+
46
+ ---
47
+
48
+ ## Step 3 — `index.js` Patterns
49
+
50
+ ### With pages (routing)
51
+ ```js
52
+ import React from 'react';
53
+ import { Route, Switch, useRouteMatch } from 'react-router-dom';
54
+ import Boundary from '../../common/ErrorBoundaries/Boundary';
55
+ import { XxxProvider } from './context/XxxContext'; // only if context exists
56
+ import XxxListPage from './pages/XxxListPage';
57
+ import XxxDetailPage from './pages/XxxDetailPage';
58
+
59
+ const Xxx = () => {
60
+ const match = useRouteMatch();
61
+ return (
62
+ <Boundary fallbackType="default">
63
+ <XxxProvider> {/* only if context exists */}
64
+ <Switch>
65
+ <Route exact path={match.path} component={XxxListPage} />
66
+ <Route path={`${match.path}/:id`} component={XxxDetailPage} />
67
+ </Switch>
68
+ </XxxProvider>
69
+ </Boundary>
70
+ );
71
+ };
72
+
73
+ export default Xxx;
74
+ ```
75
+
76
+ ### Without pages (no routing)
77
+ ```js
78
+ import React from 'react';
79
+ import Boundary from '../../common/ErrorBoundaries/Boundary';
80
+
81
+ const Xxx = () => {
82
+ // module logic lives here directly
83
+ return (
84
+ <Boundary fallbackType="default">
85
+ {/* component UI */}
86
+ </Boundary>
87
+ );
88
+ };
89
+
90
+ export default Xxx;
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Step 4 — Hook Conventions (`hooks/`)
96
+
97
+ - **API hooks** → file named `useXxxApi.js`. Contains `useQuery` and/or `useMutation`. Both can live in the same file. API call function defined in the same file.
98
+ - **Custom hooks** → file named `useXxx.js`. Contains business logic, derived state, orchestration.
99
+ - No separate `api/` folder — API calls live inside the hook file directly.
100
+
101
+ ```js
102
+ // hooks/useXxxApi.js
103
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
104
+ import { showMessage } from 'h-commons';
105
+ import { HiverAxiosInstance } from '../../../common';
106
+ import { QUERY_KEYS } from '../constants';
107
+
108
+ const fetchXxxList = (params) =>
109
+ HiverAxiosInstance({ url: '/api/v1/xxx', method: 'GET', params });
110
+
111
+ export const useXxxListApi = (id) =>
112
+ useQuery({
113
+ queryKey: [QUERY_KEYS.XXX_LIST, id],
114
+ queryFn: () => fetchXxxList({ id }),
115
+ enabled: !!id,
116
+ });
117
+
118
+ const createXxx = (payload) =>
119
+ HiverAxiosInstance({ url: '/api/v1/xxx', method: 'POST', data: payload });
120
+
121
+ export const useXxxCreateApi = (id, onSuccess) => {
122
+ const queryClient = useQueryClient();
123
+ return useMutation({
124
+ mutationFn: createXxx,
125
+ onSuccess: () => {
126
+ showMessage({ message: 'Created successfully' });
127
+ if (typeof onSuccess === 'function') onSuccess();
128
+ },
129
+ onSettled: () => {
130
+ queryClient.invalidateQueries([QUERY_KEYS.XXX_LIST, id]);
131
+ },
132
+ });
133
+ };
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Step 5 — Context (optional)
139
+
140
+ Only add when local state is shared across multiple components and is too complex for `useState`.
141
+
142
+ ```js
143
+ // context/XxxContext.js
144
+ import React, { useMemo, useReducer } from 'react';
145
+
146
+ const initialState = { fieldA: '', fieldB: null };
147
+
148
+ const xxxReducer = (state, action) => {
149
+ switch (action.type) {
150
+ case 'UPDATE_FIELD': return { ...state, ...action.payload };
151
+ case 'RESET': return initialState;
152
+ default: return state;
153
+ }
154
+ };
155
+
156
+ const XxxContext = React.createContext();
157
+
158
+ export const useXxxContext = () => React.useContext(XxxContext);
159
+
160
+ export const XxxProvider = ({ children }) => {
161
+ const [state, dispatch] = useReducer(xxxReducer, initialState);
162
+ const value = useMemo(() => ({
163
+ state,
164
+ updateField: (payload) => dispatch({ type: 'UPDATE_FIELD', payload }),
165
+ reset: () => dispatch({ type: 'RESET' }),
166
+ }), [state]);
167
+
168
+ return <XxxContext.Provider value={value}>{children}</XxxContext.Provider>;
169
+ };
170
+ ```
171
+
172
+ ---
173
+
174
+ ## Step 6 — Feature Flag (if needed)
175
+
176
+ **1. Add key** to `modules/common/constants/featureConstants.js`:
177
+ ```js
178
+ export const XXX_FEATURE_KEY = 'rls-xxx-feature-name'; // rls- for release, ftr- for feature
179
+ ```
180
+
181
+ **2. Add checker** to `modules/common/helpers/featureKeys.js`:
182
+ ```js
183
+ export const isXxxFeatureEnabled = (availableFeatures) =>
184
+ isFeatureEnabled(availableFeatures, XXX_FEATURE_KEY);
185
+ ```
186
+
187
+ **3. Gate in component:**
188
+ ```js
189
+ import { useSelector } from 'react-redux';
190
+ import { availableFeaturesSelector } from 'h-commons';
191
+ import { isXxxFeatureEnabled } from '../../common/helpers/featureKeys';
192
+
193
+ const availableFeatures = useSelector(availableFeaturesSelector);
194
+ if (!isXxxFeatureEnabled(availableFeatures)) return null;
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Checklist
200
+
201
+ - [ ] Folder created only with directories that have real content
202
+ - [ ] `index.js` set up — routing if multiple views, direct logic if single view
203
+ - [ ] `constants.js` created with query keys, route paths, enums
204
+ - [ ] API hooks in `hooks/useXxxApi.js`, custom hooks in `hooks/useXxx.js`
205
+ - [ ] `context/` added only if local state is too complex for `useState`
206
+ - [ ] `pages/` added only if module has multiple distinct views
207
+ - [ ] Feature flag key added to `featureConstants.js` (if needed)
208
+ - [ ] Feature flag checker added to `featureKeys.js` (if needed)