@jahanxu/trellis 0.4.2 → 0.5.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.
Files changed (65) hide show
  1. package/dist/configurators/workflow.d.ts.map +1 -1
  2. package/dist/configurators/workflow.js +58 -1
  3. package/dist/configurators/workflow.js.map +1 -1
  4. package/dist/constants/paths.d.ts +17 -0
  5. package/dist/constants/paths.d.ts.map +1 -1
  6. package/dist/constants/paths.js +19 -0
  7. package/dist/constants/paths.js.map +1 -1
  8. package/dist/templates/claude/commands/trellis/handoff.md +90 -387
  9. package/dist/templates/claude/commands/trellis/pick-task.md +74 -444
  10. package/dist/templates/claude/hooks/inject-subagent-context.py +17 -101
  11. package/dist/templates/claude/hooks/ralph-loop.py +1 -0
  12. package/dist/templates/claude/hooks/session-start.py +170 -54
  13. package/dist/templates/iflow/commands/trellis/handoff.md +148 -0
  14. package/dist/templates/iflow/commands/trellis/pick-task.md +145 -0
  15. package/dist/templates/iflow/hooks/inject-subagent-context.py +1 -0
  16. package/dist/templates/iflow/hooks/ralph-loop.py +1 -0
  17. package/dist/templates/iflow/hooks/session-start.py +171 -0
  18. package/dist/templates/markdown/index.d.ts +9 -0
  19. package/dist/templates/markdown/index.d.ts.map +1 -1
  20. package/dist/templates/markdown/index.js +10 -0
  21. package/dist/templates/markdown/index.js.map +1 -1
  22. package/dist/templates/markdown/spec/roles/designer/index.md.txt +57 -0
  23. package/dist/templates/markdown/spec/roles/designer/mock-data-standards.md.txt +63 -0
  24. package/dist/templates/markdown/spec/roles/designer/prototype-guidelines.md.txt +49 -0
  25. package/dist/templates/markdown/spec/roles/frontend-impl/api-integration.md.txt +63 -0
  26. package/dist/templates/markdown/spec/roles/frontend-impl/index.md.txt +57 -0
  27. package/dist/templates/markdown/spec/roles/frontend-impl/prototype-to-production.md.txt +57 -0
  28. package/dist/templates/markdown/spec/roles/pm/index.md.txt +45 -0
  29. package/dist/templates/markdown/spec/roles/pm/prd-template.md.txt +64 -0
  30. package/dist/templates/markdown/spec/roles/pm/requirement-checklist.md.txt +43 -0
  31. package/dist/templates/trellis/index.d.ts +1 -0
  32. package/dist/templates/trellis/index.d.ts.map +1 -1
  33. package/dist/templates/trellis/index.js +2 -0
  34. package/dist/templates/trellis/index.js.map +1 -1
  35. package/dist/templates/trellis/scripts/add_session.py +3 -2
  36. package/dist/templates/trellis/scripts/common/cli_adapter.py +4 -4
  37. package/dist/templates/trellis/scripts/common/developer.py +4 -4
  38. package/dist/templates/trellis/scripts/common/git_context.py +7 -7
  39. package/dist/templates/trellis/scripts/common/paths.py +64 -14
  40. package/dist/templates/trellis/scripts/common/phase.py +2 -2
  41. package/dist/templates/trellis/scripts/common/registry.py +16 -16
  42. package/dist/templates/trellis/scripts/common/task_queue.py +10 -10
  43. package/dist/templates/trellis/scripts/common/task_utils.py +5 -5
  44. package/dist/templates/trellis/scripts/common/worktree.py +8 -8
  45. package/dist/templates/trellis/scripts/pool.py +214 -265
  46. package/dist/templates/trellis/scripts/task.py +3 -116
  47. package/package.json +3 -3
  48. package/dist/templates/claude/commands/trellis/before-role-work.md +0 -364
  49. package/dist/templates/trellis/VERSION +0 -1
  50. package/dist/templates/trellis/deliverables/README.md +0 -51
  51. package/dist/templates/trellis/paths.README.md +0 -277
  52. package/dist/templates/trellis/paths.yaml +0 -41
  53. package/dist/templates/trellis/pool/implementations.json +0 -5
  54. package/dist/templates/trellis/pool/prototypes.json +0 -5
  55. package/dist/templates/trellis/pool/requirements.json +0 -5
  56. package/dist/templates/trellis/scripts/common/project_paths.py +0 -189
  57. package/dist/templates/trellis/scripts/handoff_generator.py +0 -380
  58. package/dist/templates/trellis/spec/roles/designer/index.md +0 -243
  59. package/dist/templates/trellis/spec/roles/designer/mock-data-standards.md +0 -481
  60. package/dist/templates/trellis/spec/roles/designer/prototype-guidelines.md +0 -429
  61. package/dist/templates/trellis/spec/roles/frontend-impl/api-integration.md +0 -565
  62. package/dist/templates/trellis/spec/roles/frontend-impl/index.md +0 -321
  63. package/dist/templates/trellis/spec/roles/frontend-impl/state-management.md +0 -599
  64. package/dist/templates/trellis/spec/roles/pm/index.md +0 -112
  65. package/dist/templates/trellis/spec/roles/pm/prd-template.md +0 -124
@@ -1,565 +0,0 @@
1
- # API 集成指南
2
-
3
- > Frontend 角色将原型 Mock API 替换为真实 API 的指南
4
-
5
- ---
6
-
7
- ## 核心原则
8
-
9
- API 集成的目标是:
10
- 1. **定位准确** - 快速找到所有需要替换的 Mock API
11
- 2. **类型安全** - 使用 TypeScript 确保 API 响应类型正确
12
- 3. **错误处理** - 完善处理网络错误、业务错误
13
- 4. **复用性强** - API 调用逻辑集中管理,便于维护
14
-
15
- ---
16
-
17
- ## 定位 Mock API
18
-
19
- ### 1. 搜索 TODO 注释
20
-
21
- Designer 会用 TODO 注释标注需要替换的位置:
22
-
23
- ```bash
24
- # 搜索所有需要替换的 Mock API
25
- grep -r "TODO:.*Frontend.*API" src/
26
- ```
27
-
28
- **示例输出**:
29
- ```
30
- src/pages/LoginPage.tsx:45: // TODO: [Frontend] 替换为真实API调用 POST /api/auth/login
31
- src/pages/UserList.tsx:23: // TODO: [Frontend] 替换为真实API调用 GET /api/users
32
- ```
33
-
34
- ### 2. 搜索 Mock 函数调用
35
-
36
- ```bash
37
- # 搜索 mock 关键字
38
- grep -r "mock.*API\|\.mock\." src/
39
- ```
40
-
41
- ### 3. 检查 HANDOFF 文档
42
-
43
- Designer 的 HANDOFF.md 会列出所有需要替换的 API:
44
-
45
- ```markdown
46
- ## 需要Frontend补充的逻辑
47
-
48
- 1. **API集成点**
49
- - 文件:`LoginPage.tsx:45`
50
- - Mock: `mockLoginAPI()`
51
- - 真实API: `POST /api/auth/login`
52
- ```
53
-
54
- ---
55
-
56
- ## API 客户端配置
57
-
58
- ### 1. 创建 API 客户端
59
-
60
- 使用 Axios 或 Fetch 封装统一的 API 客户端:
61
-
62
- ```typescript
63
- // src/lib/api/client.ts
64
- import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
65
-
66
- const baseURL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
67
-
68
- export const apiClient: AxiosInstance = axios.create({
69
- baseURL,
70
- timeout: 10000,
71
- headers: {
72
- 'Content-Type': 'application/json',
73
- },
74
- });
75
-
76
- // 请求拦截器:添加 token
77
- apiClient.interceptors.request.use(
78
- (config) => {
79
- const token = localStorage.getItem('auth_token');
80
- if (token) {
81
- config.headers.Authorization = `Bearer ${token}`;
82
- }
83
- return config;
84
- },
85
- (error) => Promise.reject(error)
86
- );
87
-
88
- // 响应拦截器:统一错误处理
89
- apiClient.interceptors.response.use(
90
- (response) => response.data, // 直接返回 data
91
- (error) => {
92
- if (error.response?.status === 401) {
93
- // Token 过期,跳转登录
94
- localStorage.removeItem('auth_token');
95
- window.location.href = '/login';
96
- }
97
- return Promise.reject(error);
98
- }
99
- );
100
- ```
101
-
102
- ### 2. 环境变量配置
103
-
104
- ```bash
105
- # .env.development
106
- VITE_API_BASE_URL=http://localhost:3000
107
-
108
- # .env.production
109
- VITE_API_BASE_URL=https://api.example.com
110
- ```
111
-
112
- ---
113
-
114
- ## API 函数封装
115
-
116
- ### 1. 按模块组织 API
117
-
118
- ```
119
- src/lib/api/
120
- ├── client.ts # API 客户端配置
121
- ├── auth.ts # 认证相关 API
122
- ├── user.ts # 用户相关 API
123
- ├── product.ts # 产品相关 API
124
- └── index.ts # 统一导出
125
- ```
126
-
127
- ### 2. 定义 API 函数
128
-
129
- ```typescript
130
- // src/lib/api/auth.ts
131
- import { apiClient } from './client';
132
- import type { User } from '@/types/user';
133
-
134
- export interface LoginRequest {
135
- email: string;
136
- password: string;
137
- }
138
-
139
- export interface LoginResponse {
140
- user: User;
141
- token: string;
142
- }
143
-
144
- /**
145
- * 用户登录
146
- * POST /api/auth/login
147
- */
148
- export const login = async (data: LoginRequest): Promise<LoginResponse> => {
149
- return apiClient.post('/api/auth/login', data);
150
- };
151
-
152
- /**
153
- * 用户登出
154
- * POST /api/auth/logout
155
- */
156
- export const logout = async (): Promise<void> => {
157
- return apiClient.post('/api/auth/logout');
158
- };
159
-
160
- /**
161
- * 获取当前用户信息
162
- * GET /api/auth/me
163
- */
164
- export const getCurrentUser = async (): Promise<User> => {
165
- return apiClient.get('/api/auth/me');
166
- };
167
- ```
168
-
169
- ### 3. 列表 API 封装
170
-
171
- ```typescript
172
- // src/lib/api/user.ts
173
- import { apiClient } from './client';
174
- import type { User } from '@/types/user';
175
- import type { ApiListResponse } from '@/types/api';
176
-
177
- export interface GetUsersParams {
178
- page?: number;
179
- pageSize?: number;
180
- keyword?: string;
181
- }
182
-
183
- /**
184
- * 获取用户列表
185
- * GET /api/users?page=1&pageSize=10&keyword=xxx
186
- */
187
- export const getUsers = async (
188
- params: GetUsersParams = {}
189
- ): Promise<ApiListResponse<User>> => {
190
- return apiClient.get('/api/users', { params });
191
- };
192
-
193
- /**
194
- * 获取用户详情
195
- * GET /api/users/:id
196
- */
197
- export const getUserById = async (id: string): Promise<User> => {
198
- return apiClient.get(`/api/users/${id}`);
199
- };
200
- ```
201
-
202
- ---
203
-
204
- ## 替换 Mock API
205
-
206
- ### Before(Designer 的 Mock)
207
-
208
- ```typescript
209
- // src/pages/LoginPage.tsx
210
- import { mockLoginAPI } from '@/mocks/api/auth.mock';
211
-
212
- const LoginPage = () => {
213
- const handleSubmit = async (email: string, password: string) => {
214
- try {
215
- // TODO: [Frontend] 替换为真实API调用 POST /api/auth/login
216
- const { user, token } = await mockLoginAPI(email, password);
217
- localStorage.setItem('auth_token', token);
218
- setUser(user);
219
- } catch (error) {
220
- setError('登录失败');
221
- }
222
- };
223
-
224
- // ...
225
- };
226
- ```
227
-
228
- ### After(真实 API)
229
-
230
- ```typescript
231
- // src/pages/LoginPage.tsx
232
- import { login } from '@/lib/api/auth';
233
-
234
- const LoginPage = () => {
235
- const handleSubmit = async (email: string, password: string) => {
236
- try {
237
- const { user, token } = await login({ email, password });
238
- localStorage.setItem('auth_token', token);
239
- setUser(user);
240
- navigate('/dashboard');
241
- } catch (error) {
242
- if (error.response?.status === 401) {
243
- setError('邮箱或密码错误');
244
- } else {
245
- setError('登录失败,请稍后重试');
246
- }
247
- }
248
- };
249
-
250
- // ...
251
- };
252
- ```
253
-
254
- ---
255
-
256
- ## 错误处理规范
257
-
258
- ### 1. 错误类型定义
259
-
260
- ```typescript
261
- // src/lib/api/errors.ts
262
- export class ApiError extends Error {
263
- constructor(
264
- public status: number,
265
- public message: string,
266
- public data?: any
267
- ) {
268
- super(message);
269
- this.name = 'ApiError';
270
- }
271
- }
272
-
273
- export class NetworkError extends Error {
274
- constructor(message: string = '网络连接失败') {
275
- super(message);
276
- this.name = 'NetworkError';
277
- }
278
- }
279
- ```
280
-
281
- ### 2. 统一错误处理
282
-
283
- ```typescript
284
- // src/lib/api/client.ts(响应拦截器)
285
- apiClient.interceptors.response.use(
286
- (response) => response.data,
287
- (error) => {
288
- if (error.response) {
289
- // 服务器返回错误
290
- const { status, data } = error.response;
291
- throw new ApiError(status, data.message || '请求失败', data);
292
- } else if (error.request) {
293
- // 网络错误
294
- throw new NetworkError('网络连接失败,请检查网络设置');
295
- } else {
296
- // 其他错误
297
- throw new Error('请求失败,请稍后重试');
298
- }
299
- }
300
- );
301
- ```
302
-
303
- ### 3. 组件中处理错误
304
-
305
- ```typescript
306
- const LoginPage = () => {
307
- const [error, setError] = useState<string | null>(null);
308
-
309
- const handleSubmit = async (email: string, password: string) => {
310
- setError(null);
311
- try {
312
- await login({ email, password });
313
- } catch (err) {
314
- if (err instanceof ApiError) {
315
- // 业务错误
316
- if (err.status === 401) {
317
- setError('邮箱或密码错误');
318
- } else if (err.status === 429) {
319
- setError('请求过于频繁,请稍后重试');
320
- } else {
321
- setError(err.message);
322
- }
323
- } else if (err instanceof NetworkError) {
324
- // 网络错误
325
- setError('网络连接失败,请检查网络设置');
326
- } else {
327
- // 未知错误
328
- setError('登录失败,请稍后重试');
329
- }
330
- }
331
- };
332
-
333
- return (
334
- <form onSubmit={handleSubmit}>
335
- {error && <p className="text-red-500">{error}</p>}
336
- {/* ... */}
337
- </form>
338
- );
339
- };
340
- ```
341
-
342
- ---
343
-
344
- ## 加载状态管理
345
-
346
- ### 1. 基本加载状态
347
-
348
- ```typescript
349
- const UserList = () => {
350
- const [users, setUsers] = useState<User[]>([]);
351
- const [loading, setLoading] = useState(false);
352
- const [error, setError] = useState<string | null>(null);
353
-
354
- useEffect(() => {
355
- const fetchUsers = async () => {
356
- setLoading(true);
357
- setError(null);
358
- try {
359
- const response = await getUsers({ page: 1, pageSize: 10 });
360
- setUsers(response.data.items);
361
- } catch (err) {
362
- setError('加载用户列表失败');
363
- } finally {
364
- setLoading(false);
365
- }
366
- };
367
-
368
- fetchUsers();
369
- }, []);
370
-
371
- if (loading) return <div>加载中...</div>;
372
- if (error) return <div>错误: {error}</div>;
373
-
374
- return (
375
- <ul>
376
- {users.map((user) => (
377
- <li key={user.id}>{user.name}</li>
378
- ))}
379
- </ul>
380
- );
381
- };
382
- ```
383
-
384
- ### 2. 使用 React Query(推荐)
385
-
386
- ```typescript
387
- import { useQuery } from '@tanstack/react-query';
388
- import { getUsers } from '@/lib/api/user';
389
-
390
- const UserList = () => {
391
- const {
392
- data,
393
- isLoading,
394
- error,
395
- refetch
396
- } = useQuery({
397
- queryKey: ['users', { page: 1 }],
398
- queryFn: () => getUsers({ page: 1, pageSize: 10 }),
399
- });
400
-
401
- if (isLoading) return <div>加载中...</div>;
402
- if (error) return <div>错误: {error.message}</div>;
403
-
404
- return (
405
- <div>
406
- <button onClick={() => refetch()}>刷新</button>
407
- <ul>
408
- {data?.data.items.map((user) => (
409
- <li key={user.id}>{user.name}</li>
410
- ))}
411
- </ul>
412
- </div>
413
- );
414
- };
415
- ```
416
-
417
- ---
418
-
419
- ## 类型安全保证
420
-
421
- ### 1. API 响应类型定义
422
-
423
- ```typescript
424
- // src/types/api.ts
425
- export interface ApiResponse<T> {
426
- code: number;
427
- message: string;
428
- data: T;
429
- }
430
-
431
- export interface ApiListResponse<T> {
432
- code: number;
433
- message: string;
434
- data: {
435
- items: T[];
436
- total: number;
437
- page: number;
438
- pageSize: number;
439
- };
440
- }
441
- ```
442
-
443
- ### 2. 运行时类型验证(可选)
444
-
445
- 使用 Zod 进行运行时验证:
446
-
447
- ```typescript
448
- import { z } from 'zod';
449
-
450
- const UserSchema = z.object({
451
- id: z.string(),
452
- name: z.string(),
453
- email: z.string().email(),
454
- role: z.enum(['admin', 'user', 'guest']),
455
- });
456
-
457
- export const login = async (data: LoginRequest): Promise<LoginResponse> => {
458
- const response = await apiClient.post('/api/auth/login', data);
459
-
460
- // 验证响应数据
461
- const user = UserSchema.parse(response.user);
462
-
463
- return {
464
- user,
465
- token: response.token,
466
- };
467
- };
468
- ```
469
-
470
- ---
471
-
472
- ## 检查清单
473
-
474
- API 集成完成前,确保:
475
-
476
- - [ ] 所有 TODO 标记的 Mock API 已替换
477
- - [ ] API 客户端配置完成(baseURL、拦截器)
478
- - [ ] 环境变量配置正确
479
- - [ ] 所有 API 函数包含类型定义
480
- - [ ] 错误处理完善(网络错误、业务错误)
481
- - [ ] 加载状态管理完善
482
- - [ ] Token 刷新逻辑实现(如需要)
483
- - [ ] API 调用已测试(手动测试或单元测试)
484
-
485
- ---
486
-
487
- ## 常见问题
488
-
489
- ### Q: 如何处理 Token 过期?
490
- A: 在响应拦截器中检测 401 状态,自动刷新 Token 或跳转登录页
491
-
492
- ```typescript
493
- apiClient.interceptors.response.use(
494
- (response) => response.data,
495
- async (error) => {
496
- if (error.response?.status === 401) {
497
- const refreshToken = localStorage.getItem('refresh_token');
498
- if (refreshToken) {
499
- // 尝试刷新 Token
500
- try {
501
- const { token } = await refreshAuthToken(refreshToken);
502
- localStorage.setItem('auth_token', token);
503
- // 重试原请求
504
- return apiClient.request(error.config);
505
- } catch {
506
- // 刷新失败,跳转登录
507
- window.location.href = '/login';
508
- }
509
- } else {
510
- window.location.href = '/login';
511
- }
512
- }
513
- return Promise.reject(error);
514
- }
515
- );
516
- ```
517
-
518
- ### Q: 如何处理并发请求?
519
- A: 使用 Promise.all 或 React Query 的并发查询
520
-
521
- ```typescript
522
- // Promise.all
523
- const [users, products] = await Promise.all([
524
- getUsers(),
525
- getProducts(),
526
- ]);
527
-
528
- // React Query
529
- const usersQuery = useQuery(['users'], getUsers);
530
- const productsQuery = useQuery(['products'], getProducts);
531
- ```
532
-
533
- ### Q: 如何实现请求取消?
534
- A: 使用 AbortController
535
-
536
- ```typescript
537
- useEffect(() => {
538
- const controller = new AbortController();
539
-
540
- const fetchData = async () => {
541
- try {
542
- await apiClient.get('/api/users', {
543
- signal: controller.signal,
544
- });
545
- } catch (error) {
546
- if (error.name === 'AbortError') {
547
- // 请求被取消
548
- return;
549
- }
550
- }
551
- };
552
-
553
- fetchData();
554
-
555
- return () => controller.abort(); // 组件卸载时取消请求
556
- }, []);
557
- ```
558
-
559
- ---
560
-
561
- ## 相关文档
562
-
563
- - [错误处理规范](./error-handling.md)
564
- - [状态管理规范](./state-management.md)
565
- - [Frontend 工作规范](./index.md)