moicle 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -0
- package/assets/agents/developers/flutter-mobile-dev.md +69 -0
- package/assets/agents/developers/go-backend-dev.md +57 -0
- package/assets/agents/developers/laravel-backend-dev.md +123 -0
- package/assets/agents/developers/react-frontend-dev.md +69 -0
- package/assets/agents/developers/remix-fullstack-dev.md +69 -0
- package/assets/agents/utilities/api-designer.md +76 -0
- package/assets/agents/utilities/clean-architect.md +83 -0
- package/assets/agents/utilities/code-reviewer.md +76 -0
- package/assets/agents/utilities/db-designer.md +68 -0
- package/assets/agents/utilities/devops.md +71 -0
- package/assets/agents/utilities/docs-writer.md +75 -0
- package/assets/agents/utilities/perf-optimizer.md +87 -0
- package/assets/agents/utilities/refactor.md +173 -0
- package/assets/agents/utilities/security-audit.md +203 -0
- package/assets/agents/utilities/test-writer.md +139 -0
- package/assets/architecture/clean-architecture.md +143 -0
- package/assets/architecture/flutter-mobile.md +304 -0
- package/assets/architecture/go-backend.md +217 -0
- package/assets/architecture/laravel-backend.md +303 -0
- package/assets/architecture/monorepo.md +162 -0
- package/assets/architecture/react-frontend.md +268 -0
- package/assets/architecture/remix-fullstack.md +272 -0
- package/assets/commands/bootstrap.md +98 -0
- package/assets/commands/brainstorm.md +440 -0
- package/assets/skills/feature-workflow/SKILL.md +298 -0
- package/assets/skills/hotfix-workflow/SKILL.md +368 -0
- package/assets/templates/flutter/CLAUDE.md +454 -0
- package/assets/templates/go-gin/CLAUDE.md +244 -0
- package/assets/templates/monorepo/CLAUDE.md +362 -0
- package/assets/templates/react-vite/CLAUDE.md +304 -0
- package/assets/templates/remix/CLAUDE.md +304 -0
- package/bin/cli.js +76 -0
- package/dist/commands/disable.d.ts +3 -0
- package/dist/commands/disable.d.ts.map +1 -0
- package/dist/commands/disable.js +188 -0
- package/dist/commands/disable.js.map +1 -0
- package/dist/commands/enable.d.ts +3 -0
- package/dist/commands/enable.d.ts.map +1 -0
- package/dist/commands/enable.js +191 -0
- package/dist/commands/enable.js.map +1 -0
- package/dist/commands/install.d.ts +3 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +290 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +75 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/postinstall.d.ts +2 -0
- package/dist/commands/postinstall.d.ts.map +1 -0
- package/dist/commands/postinstall.js +25 -0
- package/dist/commands/postinstall.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +118 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/uninstall.d.ts +3 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +178 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/config.d.ts +13 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +95 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/symlink.d.ts +24 -0
- package/dist/utils/symlink.d.ts.map +1 -0
- package/dist/utils/symlink.js +313 -0
- package/dist/utils/symlink.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# React Frontend Structure
|
|
2
|
+
|
|
3
|
+
> MVVM (Model-View-ViewModel) architecture
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
{project}/
|
|
9
|
+
├── src/
|
|
10
|
+
│ ├── components/ # Shared components
|
|
11
|
+
│ │ └── ui/ # Base UI (Button, Input...)
|
|
12
|
+
│ ├── config/ # App configuration
|
|
13
|
+
│ │ ├── app.config.ts
|
|
14
|
+
│ │ └── routes.config.ts
|
|
15
|
+
│ ├── core/
|
|
16
|
+
│ │ ├── mvvm/
|
|
17
|
+
│ │ │ └── ViewModel.ts # ViewModel factory
|
|
18
|
+
│ │ ├── context/ # Global contexts
|
|
19
|
+
│ │ ├── hooks/ # Core hooks
|
|
20
|
+
│ │ ├── interfaces/ # Core interfaces
|
|
21
|
+
│ │ ├── enums/ # Core enums
|
|
22
|
+
│ │ ├── errors/ # Error handling
|
|
23
|
+
│ │ ├── utils/ # Core utilities
|
|
24
|
+
│ │ ├── HttpClient.ts # API client
|
|
25
|
+
│ │ └── bootstrap.ts # App initialization
|
|
26
|
+
│ ├── modules/
|
|
27
|
+
│ │ └── {module}/
|
|
28
|
+
│ │ ├── models/ # Types, interfaces, DTOs
|
|
29
|
+
│ │ ├── view-models/ # Hooks (state management)
|
|
30
|
+
│ │ ├── services/ # API calls
|
|
31
|
+
│ │ ├── components/ # Module components
|
|
32
|
+
│ │ ├── pages/ # Page components
|
|
33
|
+
│ │ └── index.ts
|
|
34
|
+
│ ├── lib/ # Third-party integrations
|
|
35
|
+
│ ├── services/ # Shared services
|
|
36
|
+
│ ├── utils/ # Shared utilities
|
|
37
|
+
│ ├── router.tsx # Route definitions
|
|
38
|
+
│ ├── index.tsx # Entry point
|
|
39
|
+
│ └── index.css # Global styles
|
|
40
|
+
├── public/
|
|
41
|
+
├── .claude/
|
|
42
|
+
├── CLAUDE.md
|
|
43
|
+
├── package.json
|
|
44
|
+
├── tsconfig.json
|
|
45
|
+
├── vite.config.ts
|
|
46
|
+
└── tailwind.config.js
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## MVVM Pattern
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
┌─────────────────────────────────────────────────┐
|
|
53
|
+
│ View │
|
|
54
|
+
│ (Pages + Components) │
|
|
55
|
+
├─────────────────────────────────────────────────┤
|
|
56
|
+
│ ViewModel │
|
|
57
|
+
│ (Hooks - state & logic) │
|
|
58
|
+
├─────────────────────────────────────────────────┤
|
|
59
|
+
│ Model │
|
|
60
|
+
│ (Types + Services) │
|
|
61
|
+
└─────────────────────────────────────────────────┘
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Data flow:**
|
|
65
|
+
1. View renders and uses ViewModel (hook)
|
|
66
|
+
2. ViewModel manages state and calls Services
|
|
67
|
+
3. Services make API calls
|
|
68
|
+
4. Model defines data types
|
|
69
|
+
|
|
70
|
+
## Module Structure
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
modules/{module}/
|
|
74
|
+
├── models/
|
|
75
|
+
│ ├── {module}.model.ts # Types & interfaces
|
|
76
|
+
│ ├── {module}.schema.ts # Zod schemas (validation)
|
|
77
|
+
│ └── index.ts
|
|
78
|
+
├── view-models/
|
|
79
|
+
│ ├── use-{module}-list.ts # List ViewModel
|
|
80
|
+
│ ├── use-{module}-form.ts # Form ViewModel (optional)
|
|
81
|
+
│ └── index.ts
|
|
82
|
+
├── services/
|
|
83
|
+
│ ├── {module}.service.ts # API functions
|
|
84
|
+
│ └── index.ts
|
|
85
|
+
├── components/
|
|
86
|
+
│ ├── {Module}Table.tsx
|
|
87
|
+
│ ├── {Module}Modal.tsx
|
|
88
|
+
│ └── index.ts
|
|
89
|
+
├── pages/
|
|
90
|
+
│ ├── {Module}ListPage.tsx
|
|
91
|
+
│ └── index.ts
|
|
92
|
+
└── index.ts
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Key Files
|
|
96
|
+
|
|
97
|
+
### models/user.model.ts
|
|
98
|
+
```tsx
|
|
99
|
+
export interface User {
|
|
100
|
+
id: string;
|
|
101
|
+
name: string;
|
|
102
|
+
email: string;
|
|
103
|
+
createdAt: string;
|
|
104
|
+
updatedAt: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface CreateUserRequest {
|
|
108
|
+
name: string;
|
|
109
|
+
email: string;
|
|
110
|
+
password: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface UserListResponse {
|
|
114
|
+
data: User[];
|
|
115
|
+
meta: {
|
|
116
|
+
currentPage: number;
|
|
117
|
+
lastPage: number;
|
|
118
|
+
perPage: number;
|
|
119
|
+
total: number;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### services/user.service.ts
|
|
125
|
+
```tsx
|
|
126
|
+
import Client from '@/core/HttpClient';
|
|
127
|
+
import { User, CreateUserRequest } from '../models/user.model';
|
|
128
|
+
|
|
129
|
+
export const createUser = async (data: CreateUserRequest): Promise<User> => {
|
|
130
|
+
return await Client.post<User>('/users', {
|
|
131
|
+
body: JSON.stringify(data),
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const deleteUser = async (id: string): Promise<void> => {
|
|
136
|
+
await Client.delete(`/users/${id}`);
|
|
137
|
+
};
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### view-models/use-user-list.ts
|
|
141
|
+
```tsx
|
|
142
|
+
import { User } from '../models/user.model';
|
|
143
|
+
import { listViewModelFactory } from '@/core/mvvm/ViewModel';
|
|
144
|
+
|
|
145
|
+
export const useUserList = () => {
|
|
146
|
+
const viewModel = listViewModelFactory<User>(
|
|
147
|
+
undefined, // viewModel config
|
|
148
|
+
'/users', // API endpoint
|
|
149
|
+
{}, // default filters
|
|
150
|
+
{ field: 'name', order: 'asc' } // default sorter
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
return { ...viewModel };
|
|
154
|
+
};
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### pages/UserListPage.tsx
|
|
158
|
+
```tsx
|
|
159
|
+
import { useState } from 'react';
|
|
160
|
+
import { User } from '../models/user.model';
|
|
161
|
+
import { useUserList } from '../view-models/use-user-list';
|
|
162
|
+
import { UserTable } from '../components/UserTable';
|
|
163
|
+
import { UserModal } from '../components/UserModal';
|
|
164
|
+
|
|
165
|
+
export const UserListPage: React.FC = () => {
|
|
166
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
167
|
+
const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
|
168
|
+
|
|
169
|
+
const {
|
|
170
|
+
items,
|
|
171
|
+
isLoading,
|
|
172
|
+
pagination,
|
|
173
|
+
handleTableChange,
|
|
174
|
+
reload,
|
|
175
|
+
handleInputSearch,
|
|
176
|
+
} = useUserList();
|
|
177
|
+
|
|
178
|
+
const handleEdit = (record: User) => {
|
|
179
|
+
setSelectedUser(record);
|
|
180
|
+
setModalOpen(true);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<div>
|
|
185
|
+
<Button onClick={() => { setSelectedUser(null); setModalOpen(true); }}>
|
|
186
|
+
Add User
|
|
187
|
+
</Button>
|
|
188
|
+
|
|
189
|
+
<UserTable
|
|
190
|
+
dataSource={items}
|
|
191
|
+
loading={isLoading}
|
|
192
|
+
pagination={pagination}
|
|
193
|
+
onChange={handleTableChange}
|
|
194
|
+
onEdit={handleEdit}
|
|
195
|
+
onReload={reload}
|
|
196
|
+
/>
|
|
197
|
+
|
|
198
|
+
<UserModal
|
|
199
|
+
open={modalOpen}
|
|
200
|
+
onOpenChange={setModalOpen}
|
|
201
|
+
user={selectedUser}
|
|
202
|
+
onSuccess={reload}
|
|
203
|
+
/>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### core/HttpClient.ts
|
|
210
|
+
```tsx
|
|
211
|
+
const BASE_URL = import.meta.env.VITE_API_URL;
|
|
212
|
+
|
|
213
|
+
const Client = {
|
|
214
|
+
get: async <T>(url: string): Promise<T> => {
|
|
215
|
+
const res = await fetch(`${BASE_URL}${url}`, {
|
|
216
|
+
headers: { 'Authorization': `Bearer ${getToken()}` },
|
|
217
|
+
});
|
|
218
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
219
|
+
return res.json();
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
post: async <T>(url: string, options?: RequestInit): Promise<T> => {
|
|
223
|
+
const res = await fetch(`${BASE_URL}${url}`, {
|
|
224
|
+
method: 'POST',
|
|
225
|
+
headers: {
|
|
226
|
+
'Content-Type': 'application/json',
|
|
227
|
+
'Authorization': `Bearer ${getToken()}`,
|
|
228
|
+
},
|
|
229
|
+
...options,
|
|
230
|
+
});
|
|
231
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
232
|
+
return res.json();
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
delete: async (url: string): Promise<void> => {
|
|
236
|
+
await fetch(`${BASE_URL}${url}`, {
|
|
237
|
+
method: 'DELETE',
|
|
238
|
+
headers: { 'Authorization': `Bearer ${getToken()}` },
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export default Client;
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Conventions
|
|
247
|
+
|
|
248
|
+
| Item | Convention | Example |
|
|
249
|
+
|------|------------|---------|
|
|
250
|
+
| Module folder | kebab-case | `spam-slug/` |
|
|
251
|
+
| Model file | kebab-case.model.ts | `spam-slug.model.ts` |
|
|
252
|
+
| Service file | kebab-case.service.ts | `spam-slug.service.ts` |
|
|
253
|
+
| ViewModel | use-kebab-case.ts | `use-spam-slug-list.ts` |
|
|
254
|
+
| Component | PascalCase.tsx | `SpamSlugTable.tsx` |
|
|
255
|
+
| Page | PascalCasePage.tsx | `SpamSlugListPage.tsx` |
|
|
256
|
+
|
|
257
|
+
## ViewModel Factory Returns
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
listViewModelFactory<T>() returns {
|
|
261
|
+
items: T[],
|
|
262
|
+
isLoading: boolean,
|
|
263
|
+
pagination: { current, pageSize, total },
|
|
264
|
+
handleTableChange: (pagination, filters, sorter) => void,
|
|
265
|
+
handleInputSearch: (field, value) => void,
|
|
266
|
+
reload: () => void,
|
|
267
|
+
}
|
|
268
|
+
```
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Remix Fullstack Structure
|
|
2
|
+
|
|
3
|
+
> Module-based Architecture
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
Research the latest doc of version Remix before writing this structure.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
{project}/
|
|
11
|
+
├── app/
|
|
12
|
+
│ ├── modules/ # Feature modules
|
|
13
|
+
│ │ ├── auth/
|
|
14
|
+
│ │ │ ├── components/ # Module-specific components
|
|
15
|
+
│ │ │ │ └── LoginForm.tsx
|
|
16
|
+
│ │ │ ├── services/ # Business logic & DB access
|
|
17
|
+
│ │ │ │ └── auth.server.ts
|
|
18
|
+
│ │ │ └── types.ts
|
|
19
|
+
│ │ └── users/
|
|
20
|
+
│ │ ├── components/
|
|
21
|
+
│ │ │ └── UserList.tsx
|
|
22
|
+
│ │ ├── services/
|
|
23
|
+
│ │ │ └── user.server.ts
|
|
24
|
+
│ │ └── types.ts
|
|
25
|
+
│ ├── routes/ # Remix routes (Thin layer)
|
|
26
|
+
│ │ ├── _index.tsx # / (home)
|
|
27
|
+
│ │ ├── _auth.tsx # Auth layout
|
|
28
|
+
│ │ ├── _auth.login.tsx # /login
|
|
29
|
+
│ │ ├── _app.tsx # App layout
|
|
30
|
+
│ │ ├── _app.users._index.tsx # /users (list)
|
|
31
|
+
│ │ └── _app.users.$id.tsx # /users/:id (detail)
|
|
32
|
+
│ ├── components/ # Shared/Global components
|
|
33
|
+
│ │ ├── ui/ # Base UI (Button, Input, etc.)
|
|
34
|
+
│ │ └── shared/ # Shared app components (Layouts, etc.)
|
|
35
|
+
│ ├── utils/ # Shared utilities
|
|
36
|
+
│ │ ├── db.server.ts # Database client
|
|
37
|
+
│ │ └── session.server.ts # Session handling
|
|
38
|
+
│ ├── styles/
|
|
39
|
+
│ │ └── tailwind.css
|
|
40
|
+
│ ├── entry.client.tsx
|
|
41
|
+
│ ├── entry.server.tsx
|
|
42
|
+
│ └── root.tsx
|
|
43
|
+
├── public/
|
|
44
|
+
├── .claude/
|
|
45
|
+
├── CLAUDE.md
|
|
46
|
+
├── package.json
|
|
47
|
+
├── remix.config.js
|
|
48
|
+
└── tailwind.config.js
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Architecture Pattern
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Route (Loader/Action)
|
|
55
|
+
↓
|
|
56
|
+
Module Service (Business Logic + DB)
|
|
57
|
+
↓
|
|
58
|
+
Database Client
|
|
59
|
+
↓
|
|
60
|
+
Database
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Flow:**
|
|
64
|
+
1. **Route**: Handles HTTP request, validation, and response. Calls Module Service.
|
|
65
|
+
2. **Module Service**: Contains business logic, calls Database.
|
|
66
|
+
3. **Module Component**: UI specific to the feature.
|
|
67
|
+
4. **Shared Component**: Generic UI used across modules.
|
|
68
|
+
|
|
69
|
+
## Route Naming
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
routes/
|
|
73
|
+
├── _index.tsx # /
|
|
74
|
+
├── _auth.tsx # Auth layout wrapper
|
|
75
|
+
├── _auth.login.tsx # /login
|
|
76
|
+
├── _auth.register.tsx # /register
|
|
77
|
+
├── _app.tsx # App layout wrapper (protected)
|
|
78
|
+
├── _app.dashboard.tsx # /dashboard
|
|
79
|
+
├── _app.users._index.tsx # /users
|
|
80
|
+
├── _app.users.$id.tsx # /users/:id
|
|
81
|
+
└── _app.users.new.tsx # /users/new
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Key Files
|
|
85
|
+
|
|
86
|
+
### modules/users/services/user.server.ts
|
|
87
|
+
```tsx
|
|
88
|
+
import { db } from '~/utils/db.server';
|
|
89
|
+
import type { CreateUserDTO, UpdateUserDTO } from '../types';
|
|
90
|
+
|
|
91
|
+
export async function getUsers() {
|
|
92
|
+
// Example using a generic query builder or ORM
|
|
93
|
+
return db.user.findMany({
|
|
94
|
+
orderBy: { createdAt: 'desc' },
|
|
95
|
+
select: { id: true, name: true, email: true },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function getUserById(id: string) {
|
|
100
|
+
return db.user.findUnique({ where: { id } });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function createUser(data: CreateUserDTO) {
|
|
104
|
+
return db.user.create({ data });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function updateUser(id: string, data: UpdateUserDTO) {
|
|
108
|
+
return db.user.update({ where: { id }, data });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function deleteUser(id: string) {
|
|
112
|
+
return db.user.delete({ where: { id } });
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### modules/users/components/UserList.tsx
|
|
117
|
+
```tsx
|
|
118
|
+
import { Link } from '@remix-run/react';
|
|
119
|
+
import type { User } from '../types';
|
|
120
|
+
|
|
121
|
+
interface UserListProps {
|
|
122
|
+
users: Pick<User, 'id' | 'name'>[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function UserList({ users }: UserListProps) {
|
|
126
|
+
return (
|
|
127
|
+
<ul className="space-y-2">
|
|
128
|
+
{users.map((user) => (
|
|
129
|
+
<li key={user.id} className="p-4 border rounded hover:bg-gray-50">
|
|
130
|
+
<Link to={user.id} className="text-blue-600 hover:underline">
|
|
131
|
+
{user.name}
|
|
132
|
+
</Link>
|
|
133
|
+
</li>
|
|
134
|
+
))}
|
|
135
|
+
</ul>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### routes/_app.users._index.tsx
|
|
141
|
+
```tsx
|
|
142
|
+
import { json } from '@remix-run/node';
|
|
143
|
+
import { useLoaderData, Link } from '@remix-run/react';
|
|
144
|
+
import { getUsers } from '~/modules/users/services/user.server';
|
|
145
|
+
import { UserList } from '~/modules/users/components/UserList';
|
|
146
|
+
|
|
147
|
+
export async function loader() {
|
|
148
|
+
const users = await getUsers();
|
|
149
|
+
return json({ users });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export default function UsersPage() {
|
|
153
|
+
const { users } = useLoaderData<typeof loader>();
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<div className="p-6">
|
|
157
|
+
<div className="flex justify-between items-center mb-6">
|
|
158
|
+
<h1 className="text-2xl font-bold">Users</h1>
|
|
159
|
+
<Link
|
|
160
|
+
to="new"
|
|
161
|
+
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
|
162
|
+
>
|
|
163
|
+
Add User
|
|
164
|
+
</Link>
|
|
165
|
+
</div>
|
|
166
|
+
<UserList users={users} />
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### routes/_app.users.new.tsx
|
|
173
|
+
```tsx
|
|
174
|
+
import { redirect, type ActionFunctionArgs } from '@remix-run/node';
|
|
175
|
+
import { Form } from '@remix-run/react';
|
|
176
|
+
import { createUser } from '~/modules/users/services/user.server';
|
|
177
|
+
|
|
178
|
+
export async function action({ request }: ActionFunctionArgs) {
|
|
179
|
+
const formData = await request.formData();
|
|
180
|
+
const name = formData.get('name') as string;
|
|
181
|
+
const email = formData.get('email') as string;
|
|
182
|
+
|
|
183
|
+
// Basic validation could go here or in service
|
|
184
|
+
if (!email || !name) {
|
|
185
|
+
return json({ error: 'Missing fields' }, { status: 400 });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
await createUser({ name, email });
|
|
189
|
+
return redirect('/users');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export default function NewUserPage() {
|
|
193
|
+
return (
|
|
194
|
+
<div className="max-w-md mx-auto mt-10">
|
|
195
|
+
<h1 className="text-xl font-bold mb-4">Create User</h1>
|
|
196
|
+
<Form method="post" className="space-y-4">
|
|
197
|
+
<div>
|
|
198
|
+
<label className="block text-sm font-medium text-gray-700">Name</label>
|
|
199
|
+
<input
|
|
200
|
+
name="name"
|
|
201
|
+
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
|
202
|
+
required
|
|
203
|
+
/>
|
|
204
|
+
</div>
|
|
205
|
+
<div>
|
|
206
|
+
<label className="block text-sm font-medium text-gray-700">Email</label>
|
|
207
|
+
<input
|
|
208
|
+
name="email"
|
|
209
|
+
type="email"
|
|
210
|
+
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
|
211
|
+
required
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
<button
|
|
215
|
+
type="submit"
|
|
216
|
+
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700"
|
|
217
|
+
>
|
|
218
|
+
Create
|
|
219
|
+
</button>
|
|
220
|
+
</Form>
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### utils/db.server.ts
|
|
227
|
+
```tsx
|
|
228
|
+
// Database client setup (e.g., Prisma, Drizzle, Kysely)
|
|
229
|
+
// This file should export the shared database instance
|
|
230
|
+
|
|
231
|
+
let db: any;
|
|
232
|
+
|
|
233
|
+
declare global {
|
|
234
|
+
var __db__: any;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (process.env.NODE_ENV === 'production') {
|
|
238
|
+
// db = new DatabaseClient();
|
|
239
|
+
} else {
|
|
240
|
+
if (!global.__db__) {
|
|
241
|
+
// global.__db__ = new DatabaseClient();
|
|
242
|
+
}
|
|
243
|
+
db = global.__db__;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export { db };
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Conventions
|
|
250
|
+
|
|
251
|
+
| Item | Convention | Example |
|
|
252
|
+
|------|------------|---------|
|
|
253
|
+
| Module Directory | `app/modules/{feature}` | `app/modules/users` |
|
|
254
|
+
| Service File | `xxx.server.ts` | `user.server.ts` |
|
|
255
|
+
| Component | PascalCase | `UserList.tsx` |
|
|
256
|
+
| Route File | lowercase.tsx | `_app.users.tsx` |
|
|
257
|
+
| Loader | `loader` export | `export async function loader()` |
|
|
258
|
+
| Action | `action` export | `export async function action()` |
|
|
259
|
+
|
|
260
|
+
## When to use this structure
|
|
261
|
+
|
|
262
|
+
**Recommended for:**
|
|
263
|
+
- Medium to Large applications
|
|
264
|
+
- Teams working on different features
|
|
265
|
+
- Applications with complex business logic
|
|
266
|
+
- When you want to keep related code (UI + Logic) together
|
|
267
|
+
|
|
268
|
+
**Benefits:**
|
|
269
|
+
- **Co-location**: Everything related to a feature is in one place.
|
|
270
|
+
- **Scalability**: Easy to add new features without cluttering global folders.
|
|
271
|
+
- **Maintainability**: Clear boundaries between features.
|
|
272
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bootstrap
|
|
3
|
+
description: Bootstrap a new project with Claude Code support
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Bootstrap New Project
|
|
7
|
+
|
|
8
|
+
You are a project bootstrapping assistant. Guide the user through creating a new project with proper Claude Code support.
|
|
9
|
+
|
|
10
|
+
## IMPORTANT: Architecture Reference
|
|
11
|
+
|
|
12
|
+
Before creating any project structure, you MUST read the architecture reference files:
|
|
13
|
+
|
|
14
|
+
- **Clean Architecture**: `~/.claude/architecture/clean-architecture.md`
|
|
15
|
+
- **Go Backend**: `~/.claude/architecture/go-backend.md`
|
|
16
|
+
- **Laravel Backend**: `~/.claude/architecture/laravel-backend.md`
|
|
17
|
+
- **React Frontend**: `~/.claude/architecture/react-frontend.md`
|
|
18
|
+
- **Remix Fullstack**: `~/.claude/architecture/remix-fullstack.md`
|
|
19
|
+
- **Flutter Mobile**: `~/.claude/architecture/flutter-mobile.md`
|
|
20
|
+
- **Monorepo**: `~/.claude/architecture/monorepo.md`
|
|
21
|
+
|
|
22
|
+
## Step 1: Select Stack
|
|
23
|
+
|
|
24
|
+
Ask the user to select their preferred stack:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Which stack would you like to use?
|
|
28
|
+
|
|
29
|
+
1. Go + Gin (Backend API)
|
|
30
|
+
2. Laravel (Backend API - PHP)
|
|
31
|
+
3. React + Vite (Frontend SPA)
|
|
32
|
+
4. Remix (Full-stack React)
|
|
33
|
+
5. Flutter (Mobile/Desktop App)
|
|
34
|
+
6. Monorepo (Frontend + Backend)
|
|
35
|
+
|
|
36
|
+
Enter number (1-6):
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Step 2: Read Architecture Reference
|
|
40
|
+
|
|
41
|
+
Based on selection, READ the corresponding architecture file:
|
|
42
|
+
|
|
43
|
+
| Stack | Architecture File |
|
|
44
|
+
|-------|-------------------|
|
|
45
|
+
| Go + Gin | `go-backend.md` + `clean-architecture.md` |
|
|
46
|
+
| Laravel | `laravel-backend.md` + `clean-architecture.md` |
|
|
47
|
+
| React + Vite | `react-frontend.md` + `clean-architecture.md` |
|
|
48
|
+
| Remix | `remix-fullstack.md` + `clean-architecture.md` |
|
|
49
|
+
| Flutter | `flutter-mobile.md` + `clean-architecture.md` |
|
|
50
|
+
| Monorepo | `monorepo.md` + relevant app architectures |
|
|
51
|
+
|
|
52
|
+
## Step 3: Get Project Info
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
What is your project name?
|
|
56
|
+
(Use lowercase with hyphens, e.g., my-awesome-project)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Step 4: Select Features
|
|
60
|
+
|
|
61
|
+
Present relevant features based on stack (see architecture file for details).
|
|
62
|
+
|
|
63
|
+
## Step 5: Create Project
|
|
64
|
+
|
|
65
|
+
Create project structure **exactly as defined in architecture reference file**.
|
|
66
|
+
|
|
67
|
+
## Step 6: Copy Architecture Reference
|
|
68
|
+
|
|
69
|
+
Copy relevant architecture files to project:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
{project}/.claude/
|
|
73
|
+
├── agents/
|
|
74
|
+
└── architecture/
|
|
75
|
+
├── clean-architecture.md
|
|
76
|
+
└── {stack}-specific.md
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Step 7: Setup CLAUDE.md
|
|
80
|
+
|
|
81
|
+
Create CLAUDE.md that references architecture:
|
|
82
|
+
|
|
83
|
+
```markdown
|
|
84
|
+
# {Project Name}
|
|
85
|
+
|
|
86
|
+
## Architecture
|
|
87
|
+
|
|
88
|
+
This project follows Clean Architecture. See:
|
|
89
|
+
- `.claude/architecture/clean-architecture.md`
|
|
90
|
+
- `.claude/architecture/{stack}.md`
|
|
91
|
+
|
|
92
|
+
## Quick Start
|
|
93
|
+
...
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Step 8: Initialize & Final Output
|
|
97
|
+
|
|
98
|
+
Run init commands and display success message with next steps.
|