@walwal-harness/cli 7.1.38 → 7.1.40
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/HR-Resource/cto/SKILL.md +8 -0
- package/HR-Resource/ops/SKILL.md +18 -0
- package/HR-Resource/ops-prompt-inspector/SKILL.md +100 -0
- package/HR-Resource/ops-prompt-inspector/agents/installation-agent.json +7 -0
- package/HR-Resource/ops-prompt-inspector/assets/PromptInspector.tsx +599 -0
- package/HR-Resource/ops-prompt-inspector/references/handler-templates.md +255 -0
- package/HR-Resource/ops-prompt-inspector/scripts/discover_apis.py +509 -0
- package/HR-Resource/ops-prompt-inspector/scripts/install.sh +32 -0
- package/HR-Resource/ops-prompt-inspector/scripts/setup.py +318 -0
- package/apps/harness-dashboard/package-lock.json +43 -42
- package/assets/templates/AGENTS-ko.md.template +15 -2
- package/assets/templates/AGENTS.md.template +17 -2
- package/bin/init.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# API Handler Templates
|
|
2
|
+
|
|
3
|
+
## Success/Failure Handler Types
|
|
4
|
+
|
|
5
|
+
### 1. Toast Messages
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
// Using react-hot-toast
|
|
9
|
+
import toast from 'react-hot-toast';
|
|
10
|
+
|
|
11
|
+
// Success
|
|
12
|
+
toast.success('${message}');
|
|
13
|
+
|
|
14
|
+
// Error
|
|
15
|
+
toast.error('${message}');
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
// Using sonner
|
|
20
|
+
import { toast } from 'sonner';
|
|
21
|
+
|
|
22
|
+
// Success
|
|
23
|
+
toast.success('${message}');
|
|
24
|
+
|
|
25
|
+
// Error
|
|
26
|
+
toast.error('${message}');
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
// Using react-toastify
|
|
31
|
+
import { toast } from 'react-toastify';
|
|
32
|
+
|
|
33
|
+
// Success
|
|
34
|
+
toast.success('${message}');
|
|
35
|
+
|
|
36
|
+
// Error
|
|
37
|
+
toast.error('${message}');
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Alert/Popup Dialogs
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
// Native browser alert
|
|
44
|
+
alert('${message}');
|
|
45
|
+
|
|
46
|
+
// Using shadcn/ui AlertDialog
|
|
47
|
+
import {
|
|
48
|
+
AlertDialog,
|
|
49
|
+
AlertDialogAction,
|
|
50
|
+
AlertDialogContent,
|
|
51
|
+
AlertDialogDescription,
|
|
52
|
+
AlertDialogFooter,
|
|
53
|
+
AlertDialogHeader,
|
|
54
|
+
AlertDialogTitle,
|
|
55
|
+
} from "@/components/ui/alert-dialog";
|
|
56
|
+
|
|
57
|
+
<AlertDialog open={isOpen} onOpenChange={setIsOpen}>
|
|
58
|
+
<AlertDialogContent>
|
|
59
|
+
<AlertDialogHeader>
|
|
60
|
+
<AlertDialogTitle>${title}</AlertDialogTitle>
|
|
61
|
+
<AlertDialogDescription>${message}</AlertDialogDescription>
|
|
62
|
+
</AlertDialogHeader>
|
|
63
|
+
<AlertDialogFooter>
|
|
64
|
+
<AlertDialogAction onClick={() => setIsOpen(false)}>
|
|
65
|
+
확인
|
|
66
|
+
</AlertDialogAction>
|
|
67
|
+
</AlertDialogFooter>
|
|
68
|
+
</AlertDialogContent>
|
|
69
|
+
</AlertDialog>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 3. Page Navigation
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
// Using Next.js App Router
|
|
76
|
+
import { useRouter } from 'next/navigation';
|
|
77
|
+
|
|
78
|
+
const router = useRouter();
|
|
79
|
+
|
|
80
|
+
// Success redirect
|
|
81
|
+
router.push('${successPath}');
|
|
82
|
+
|
|
83
|
+
// With replace (no back button)
|
|
84
|
+
router.replace('${successPath}');
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
// Using Next.js Pages Router
|
|
89
|
+
import { useRouter } from 'next/router';
|
|
90
|
+
|
|
91
|
+
const router = useRouter();
|
|
92
|
+
router.push('${successPath}');
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 4. State Update
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
// Update local state
|
|
99
|
+
setData(response.data);
|
|
100
|
+
setLoading(false);
|
|
101
|
+
setError(null);
|
|
102
|
+
|
|
103
|
+
// With React Query
|
|
104
|
+
queryClient.invalidateQueries({ queryKey: ['${queryKey}'] });
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Combined Handler Patterns
|
|
110
|
+
|
|
111
|
+
### Pattern A: Toast + State Update
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
const handleSuccess = (data: ResponseType) => {
|
|
115
|
+
toast.success('${successMessage}');
|
|
116
|
+
setData(data);
|
|
117
|
+
setLoading(false);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const handleError = (error: Error) => {
|
|
121
|
+
toast.error('${errorMessage}');
|
|
122
|
+
setError(error.message);
|
|
123
|
+
setLoading(false);
|
|
124
|
+
};
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Pattern B: Toast + Navigation
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
const handleSuccess = () => {
|
|
131
|
+
toast.success('${successMessage}');
|
|
132
|
+
router.push('${successPath}');
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const handleError = (error: Error) => {
|
|
136
|
+
toast.error(error.message || '${errorMessage}');
|
|
137
|
+
};
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Pattern C: Popup Dialog + Navigation
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
const handleSuccess = () => {
|
|
144
|
+
setDialogConfig({
|
|
145
|
+
open: true,
|
|
146
|
+
title: '${successTitle}',
|
|
147
|
+
message: '${successMessage}',
|
|
148
|
+
onConfirm: () => router.push('${successPath}'),
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const handleError = (error: Error) => {
|
|
153
|
+
setDialogConfig({
|
|
154
|
+
open: true,
|
|
155
|
+
title: '${errorTitle}',
|
|
156
|
+
message: error.message || '${errorMessage}',
|
|
157
|
+
onConfirm: () => setDialogConfig({ open: false }),
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Trigger Patterns
|
|
165
|
+
|
|
166
|
+
### onLoad (페이지 로딩 시)
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
// Using useEffect
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
fetchData();
|
|
172
|
+
}, []);
|
|
173
|
+
|
|
174
|
+
// Using React Query
|
|
175
|
+
const { data, isLoading, error } = useQuery({
|
|
176
|
+
queryKey: ['${queryKey}'],
|
|
177
|
+
queryFn: () => fetch('${apiPath}').then(res => res.json()),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Using SWR
|
|
181
|
+
const { data, error, isLoading } = useSWR('${apiPath}', fetcher);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### onClick (클릭 시)
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
<button onClick={async () => {
|
|
188
|
+
try {
|
|
189
|
+
setLoading(true);
|
|
190
|
+
const response = await axios.${method}('${apiPath}', ${hasBody ? 'payload' : ''});
|
|
191
|
+
handleSuccess(response.data);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
handleError(error);
|
|
194
|
+
}
|
|
195
|
+
}}>
|
|
196
|
+
${buttonText}
|
|
197
|
+
</button>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### onSubmit (폼 제출 시)
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
204
|
+
e.preventDefault();
|
|
205
|
+
try {
|
|
206
|
+
setLoading(true);
|
|
207
|
+
const response = await axios.${method}('${apiPath}', formData);
|
|
208
|
+
handleSuccess(response.data);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
handleError(error);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
<form onSubmit={handleSubmit}>
|
|
215
|
+
{/* form fields */}
|
|
216
|
+
<button type="submit" disabled={loading}>
|
|
217
|
+
{loading ? '처리 중...' : '${submitText}'}
|
|
218
|
+
</button>
|
|
219
|
+
</form>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### onBlur (포커스 해제 시)
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
<input
|
|
226
|
+
onBlur={async (e) => {
|
|
227
|
+
const value = e.target.value;
|
|
228
|
+
if (value !== previousValue) {
|
|
229
|
+
try {
|
|
230
|
+
await axios.${method}('${apiPath}', { value });
|
|
231
|
+
handleSuccess();
|
|
232
|
+
} catch (error) {
|
|
233
|
+
handleError(error);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}}
|
|
237
|
+
/>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### onChange with Debounce (입력 변경 시)
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
import { useDebouncedCallback } from 'use-debounce';
|
|
244
|
+
|
|
245
|
+
const debouncedSave = useDebouncedCallback(async (value: string) => {
|
|
246
|
+
try {
|
|
247
|
+
await axios.${method}('${apiPath}', { value });
|
|
248
|
+
handleSuccess();
|
|
249
|
+
} catch (error) {
|
|
250
|
+
handleError(error);
|
|
251
|
+
}
|
|
252
|
+
}, 500);
|
|
253
|
+
|
|
254
|
+
<input onChange={(e) => debouncedSave(e.target.value)} />
|
|
255
|
+
```
|