work-agent 0.1.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 (245) hide show
  1. package/README.md +234 -0
  2. package/app/(admin)/approvals/page.tsx +16 -0
  3. package/app/(admin)/audit/page.tsx +18 -0
  4. package/app/(admin)/layout.tsx +47 -0
  5. package/app/(admin)/scheduled-tasks/page.tsx +17 -0
  6. package/app/(admin)/settings/page.tsx +46 -0
  7. package/app/(admin)/skills/[name]/page.tsx +378 -0
  8. package/app/(admin)/skills/page.tsx +406 -0
  9. package/app/(admin)/statistics/page.tsx +416 -0
  10. package/app/(admin)/tickets/[id]/page.tsx +348 -0
  11. package/app/(admin)/tickets/new/page.tsx +309 -0
  12. package/app/(admin)/tickets/page.tsx +27 -0
  13. package/app/api/audit/route.ts +30 -0
  14. package/app/api/auth/feishu/callback/route.ts +72 -0
  15. package/app/api/auth/feishu/login/route.ts +17 -0
  16. package/app/api/auth/feishu/sso/route.ts +78 -0
  17. package/app/api/auth/login/route.ts +85 -0
  18. package/app/api/auth/oauth/route.ts +168 -0
  19. package/app/api/config/providers/route.ts +105 -0
  20. package/app/api/config/route.ts +115 -0
  21. package/app/api/config/status/route.ts +56 -0
  22. package/app/api/config/test/route.ts +212 -0
  23. package/app/api/documents/[id]/route.ts +88 -0
  24. package/app/api/documents/route.ts +53 -0
  25. package/app/api/health/route.ts +32 -0
  26. package/app/api/knowledge/[id]/route.ts +152 -0
  27. package/app/api/knowledge/from-session/route.ts +27 -0
  28. package/app/api/knowledge/route.ts +100 -0
  29. package/app/api/market/knowledge/[id]/route.ts +92 -0
  30. package/app/api/market/knowledge/route.ts +130 -0
  31. package/app/api/marketplace/skills/[id]/approve/route.ts +68 -0
  32. package/app/api/marketplace/skills/[id]/certify/route.ts +54 -0
  33. package/app/api/marketplace/skills/[id]/install/route.ts +180 -0
  34. package/app/api/marketplace/skills/[id]/promote-to-system/route.ts +219 -0
  35. package/app/api/marketplace/skills/[id]/rate/route.ts +90 -0
  36. package/app/api/marketplace/skills/[id]/ratings/route.ts +55 -0
  37. package/app/api/marketplace/skills/[id]/reject/route.ts +68 -0
  38. package/app/api/marketplace/skills/[id]/route.ts +177 -0
  39. package/app/api/marketplace/skills/route.ts +235 -0
  40. package/app/api/memory/route.ts +40 -0
  41. package/app/api/my/files/[id]/route.ts +52 -0
  42. package/app/api/my/files/route.ts +230 -0
  43. package/app/api/my/knowledge/route.ts +36 -0
  44. package/app/api/pi-chat/route.ts +443 -0
  45. package/app/api/recommend/route.ts +38 -0
  46. package/app/api/scheduled-tasks/[id]/execute/route.ts +132 -0
  47. package/app/api/scheduled-tasks/[id]/route.ts +165 -0
  48. package/app/api/scheduled-tasks/[id]/toggle/route.ts +53 -0
  49. package/app/api/scheduled-tasks/route.ts +101 -0
  50. package/app/api/sessions/[id]/messages/route.ts +212 -0
  51. package/app/api/sessions/route.ts +101 -0
  52. package/app/api/share/file/[id]/route.ts +37 -0
  53. package/app/api/skills/[name]/execute/route.ts +121 -0
  54. package/app/api/skills/[name]/route.ts +167 -0
  55. package/app/api/skills/create/route.ts +65 -0
  56. package/app/api/skills/generate/route.ts +405 -0
  57. package/app/api/skills/installed/route.ts +151 -0
  58. package/app/api/skills/route.ts +174 -0
  59. package/app/api/skills/translate/route.ts +40 -0
  60. package/app/api/skills/user/[name]/route.ts +159 -0
  61. package/app/api/skills/user/route.ts +90 -0
  62. package/app/api/statistics/route.ts +94 -0
  63. package/app/api/task-executions/[id]/route.ts +34 -0
  64. package/app/api/task-executions/route.ts +29 -0
  65. package/app/api/tickets/[id]/approve/route.ts +129 -0
  66. package/app/api/tickets/[id]/execute/route.ts +201 -0
  67. package/app/api/tickets/[id]/route.ts +127 -0
  68. package/app/api/tickets/route.ts +103 -0
  69. package/app/api/user/skills/route.ts +175 -0
  70. package/app/api/users/route.ts +80 -0
  71. package/app/chat/page.tsx +5 -0
  72. package/app/globals.css +84 -0
  73. package/app/h5/layout.tsx +5 -0
  74. package/app/h5/mobile-approvals-page.tsx +167 -0
  75. package/app/h5/mobile-chat-page.tsx +951 -0
  76. package/app/h5/mobile-profile-page.tsx +147 -0
  77. package/app/h5/mobile-tickets-page.tsx +121 -0
  78. package/app/h5/page.tsx +23 -0
  79. package/app/h5/ticket-action-buttons.tsx +80 -0
  80. package/app/layout.tsx +26 -0
  81. package/app/login/page.tsx +318 -0
  82. package/app/market/knowledge/[id]/page.tsx +77 -0
  83. package/app/market/knowledge/page.tsx +358 -0
  84. package/app/market/layout.tsx +29 -0
  85. package/app/market/page.tsx +18 -0
  86. package/app/market/skills/page.tsx +397 -0
  87. package/app/my/files/page.tsx +511 -0
  88. package/app/my/knowledge/[id]/page.tsx +271 -0
  89. package/app/my/knowledge/new/page.tsx +234 -0
  90. package/app/my/knowledge/page.tsx +248 -0
  91. package/app/my/layout.tsx +32 -0
  92. package/app/my/memory/page.tsx +164 -0
  93. package/app/my/page.tsx +18 -0
  94. package/app/my/scheduled-tasks/[id]/edit/page.tsx +290 -0
  95. package/app/my/scheduled-tasks/[id]/executions/page.tsx +275 -0
  96. package/app/my/scheduled-tasks/[id]/page.tsx +284 -0
  97. package/app/my/scheduled-tasks/new/page.tsx +230 -0
  98. package/app/my/scheduled-tasks/page.tsx +27 -0
  99. package/app/my/skills/[name]/page.tsx +320 -0
  100. package/app/my/skills/new/page.tsx +394 -0
  101. package/app/my/skills/page.tsx +303 -0
  102. package/app/page.tsx +2288 -0
  103. package/app/share/[sessionId]/page.tsx +226 -0
  104. package/app/share/file/[id]/page.tsx +140 -0
  105. package/bin/README.md +63 -0
  106. package/bin/generate-api-system +300 -0
  107. package/bin/postinstall.js +95 -0
  108. package/bin/work-agent.js +173 -0
  109. package/components/ai-elements/agent.tsx +142 -0
  110. package/components/ai-elements/artifact.tsx +149 -0
  111. package/components/ai-elements/attachments.tsx +427 -0
  112. package/components/ai-elements/audio-player.tsx +232 -0
  113. package/components/ai-elements/canvas.tsx +26 -0
  114. package/components/ai-elements/chain-of-thought.tsx +223 -0
  115. package/components/ai-elements/checkpoint.tsx +72 -0
  116. package/components/ai-elements/code-block.tsx +555 -0
  117. package/components/ai-elements/commit.tsx +449 -0
  118. package/components/ai-elements/confirmation.tsx +173 -0
  119. package/components/ai-elements/connection.tsx +28 -0
  120. package/components/ai-elements/context.tsx +410 -0
  121. package/components/ai-elements/controls.tsx +19 -0
  122. package/components/ai-elements/conversation.tsx +167 -0
  123. package/components/ai-elements/edge.tsx +144 -0
  124. package/components/ai-elements/environment-variables.tsx +325 -0
  125. package/components/ai-elements/file-tree.tsx +298 -0
  126. package/components/ai-elements/image.tsx +25 -0
  127. package/components/ai-elements/inline-citation.tsx +294 -0
  128. package/components/ai-elements/jsx-preview.tsx +250 -0
  129. package/components/ai-elements/message.tsx +367 -0
  130. package/components/ai-elements/mic-selector.tsx +372 -0
  131. package/components/ai-elements/model-selector.tsx +214 -0
  132. package/components/ai-elements/node.tsx +72 -0
  133. package/components/ai-elements/open-in-chat.tsx +367 -0
  134. package/components/ai-elements/package-info.tsx +235 -0
  135. package/components/ai-elements/panel.tsx +16 -0
  136. package/components/ai-elements/persona.tsx +280 -0
  137. package/components/ai-elements/plan.tsx +144 -0
  138. package/components/ai-elements/prompt-input.tsx +1341 -0
  139. package/components/ai-elements/queue.tsx +275 -0
  140. package/components/ai-elements/reasoning.tsx +355 -0
  141. package/components/ai-elements/sandbox.tsx +133 -0
  142. package/components/ai-elements/schema-display.tsx +473 -0
  143. package/components/ai-elements/shimmer.tsx +78 -0
  144. package/components/ai-elements/snippet.tsx +141 -0
  145. package/components/ai-elements/sources.tsx +78 -0
  146. package/components/ai-elements/speech-input.tsx +324 -0
  147. package/components/ai-elements/stack-trace.tsx +531 -0
  148. package/components/ai-elements/suggestion.tsx +58 -0
  149. package/components/ai-elements/task.tsx +88 -0
  150. package/components/ai-elements/terminal.tsx +277 -0
  151. package/components/ai-elements/test-results.tsx +497 -0
  152. package/components/ai-elements/tool.tsx +174 -0
  153. package/components/ai-elements/toolbar.tsx +17 -0
  154. package/components/ai-elements/transcription.tsx +126 -0
  155. package/components/ai-elements/voice-selector.tsx +525 -0
  156. package/components/ai-elements/web-preview.tsx +282 -0
  157. package/components/audit-log-list.tsx +114 -0
  158. package/components/chat/EmptyPreviewState.tsx +12 -0
  159. package/components/chat/KnowledgePickerDialog.tsx +464 -0
  160. package/components/chat/KnowledgePreview.tsx +70 -0
  161. package/components/chat/KnowledgePreviewPanel.tsx +86 -0
  162. package/components/chat/MentionInput.tsx +309 -0
  163. package/components/chat/OrganizeDialog.tsx +258 -0
  164. package/components/chat/RecommendationBanner.tsx +94 -0
  165. package/components/chat/SaveToKnowledgeDialog.tsx +193 -0
  166. package/components/chat/SkillSelector.tsx +305 -0
  167. package/components/chat/SkillSwitcher.tsx +163 -0
  168. package/components/client-layout.tsx +15 -0
  169. package/components/knowledge/KnowledgeMetadataPanel.tsx +293 -0
  170. package/components/layout-wrapper.tsx +18 -0
  171. package/components/mobile-layout.tsx +62 -0
  172. package/components/scheduled-task-list.tsx +356 -0
  173. package/components/setup-guide.tsx +484 -0
  174. package/components/sub-nav.tsx +54 -0
  175. package/components/ticket-detail-content.tsx +383 -0
  176. package/components/ticket-list.tsx +366 -0
  177. package/components/top-nav.tsx +132 -0
  178. package/components/ui/accordion.tsx +58 -0
  179. package/components/ui/alert.tsx +59 -0
  180. package/components/ui/avatar.tsx +50 -0
  181. package/components/ui/badge.tsx +36 -0
  182. package/components/ui/button-group.tsx +83 -0
  183. package/components/ui/button.tsx +57 -0
  184. package/components/ui/card.tsx +91 -0
  185. package/components/ui/carousel.tsx +262 -0
  186. package/components/ui/collapsible.tsx +11 -0
  187. package/components/ui/command.tsx +153 -0
  188. package/components/ui/dialog.tsx +122 -0
  189. package/components/ui/dropdown-menu.tsx +200 -0
  190. package/components/ui/hover-card.tsx +29 -0
  191. package/components/ui/input-group.tsx +170 -0
  192. package/components/ui/input.tsx +22 -0
  193. package/components/ui/label.tsx +26 -0
  194. package/components/ui/popover.tsx +31 -0
  195. package/components/ui/progress.tsx +28 -0
  196. package/components/ui/scroll-area.tsx +48 -0
  197. package/components/ui/select.tsx +174 -0
  198. package/components/ui/separator.tsx +31 -0
  199. package/components/ui/spinner.tsx +16 -0
  200. package/components/ui/switch.tsx +29 -0
  201. package/components/ui/table.tsx +120 -0
  202. package/components/ui/tabs.tsx +55 -0
  203. package/components/ui/textarea.tsx +22 -0
  204. package/components/ui/tooltip.tsx +30 -0
  205. package/components/welcome-guide.tsx +182 -0
  206. package/components.json +24 -0
  207. package/lib/command-parser.ts +331 -0
  208. package/lib/dangerous-commands.ts +672 -0
  209. package/lib/db.ts +2250 -0
  210. package/lib/feishu-auth.ts +135 -0
  211. package/lib/file-storage.ts +306 -0
  212. package/lib/file-tool.ts +583 -0
  213. package/lib/knowledge-tool.ts +152 -0
  214. package/lib/knowledge-types.ts +66 -0
  215. package/lib/market-client.ts +313 -0
  216. package/lib/market-db.ts +736 -0
  217. package/lib/market-types.ts +51 -0
  218. package/lib/memory-tool.ts +211 -0
  219. package/lib/memory.ts +197 -0
  220. package/lib/pi-config.ts +436 -0
  221. package/lib/pi-session.ts +799 -0
  222. package/lib/pinyin.ts +13 -0
  223. package/lib/recommendation.ts +227 -0
  224. package/lib/risk-estimator.ts +350 -0
  225. package/lib/scheduled-task-tool.ts +184 -0
  226. package/lib/scheduler-init.ts +43 -0
  227. package/lib/scheduler.ts +416 -0
  228. package/lib/secure-bash-tool.ts +413 -0
  229. package/lib/skill-engine.ts +396 -0
  230. package/lib/skill-generator.ts +269 -0
  231. package/lib/skill-loader.ts +234 -0
  232. package/lib/skill-tool.ts +188 -0
  233. package/lib/skill-types.ts +82 -0
  234. package/lib/skills-init.ts +58 -0
  235. package/lib/ticket-tool.ts +246 -0
  236. package/lib/user-skill-types.ts +30 -0
  237. package/lib/user-skills.ts +362 -0
  238. package/lib/utils.ts +6 -0
  239. package/lib/workflow.ts +154 -0
  240. package/lib/zip-tool.ts +191 -0
  241. package/next.config.js +8 -0
  242. package/package.json +106 -0
  243. package/public/.gitkeep +1 -0
  244. package/public/icon.svg +1 -0
  245. package/tsconfig.json +42 -0
@@ -0,0 +1,449 @@
1
+ "use client";
2
+
3
+ import type { ComponentProps, HTMLAttributes } from "react";
4
+
5
+ import { Avatar, AvatarFallback } from "@/components/ui/avatar";
6
+ import { Button } from "@/components/ui/button";
7
+ import {
8
+ Collapsible,
9
+ CollapsibleContent,
10
+ CollapsibleTrigger,
11
+ } from "@/components/ui/collapsible";
12
+ import { cn } from "@/lib/utils";
13
+ import {
14
+ CheckIcon,
15
+ CopyIcon,
16
+ FileIcon,
17
+ GitCommitIcon,
18
+ MinusIcon,
19
+ PlusIcon,
20
+ } from "lucide-react";
21
+ import { useCallback, useEffect, useRef, useState } from "react";
22
+
23
+ export type CommitProps = ComponentProps<typeof Collapsible>;
24
+
25
+ export const Commit = ({ className, children, ...props }: CommitProps) => (
26
+ <Collapsible
27
+ className={cn("rounded-lg border bg-background", className)}
28
+ {...props}
29
+ >
30
+ {children}
31
+ </Collapsible>
32
+ );
33
+
34
+ export type CommitHeaderProps = ComponentProps<typeof CollapsibleTrigger>;
35
+
36
+ export const CommitHeader = ({
37
+ className,
38
+ children,
39
+ ...props
40
+ }: CommitHeaderProps) => (
41
+ <CollapsibleTrigger asChild {...props}>
42
+ <div
43
+ className={cn(
44
+ "group flex cursor-pointer items-center justify-between gap-4 p-3 text-left transition-colors hover:opacity-80",
45
+ className
46
+ )}
47
+ >
48
+ {children}
49
+ </div>
50
+ </CollapsibleTrigger>
51
+ );
52
+
53
+ export type CommitHashProps = HTMLAttributes<HTMLSpanElement>;
54
+
55
+ export const CommitHash = ({
56
+ className,
57
+ children,
58
+ ...props
59
+ }: CommitHashProps) => (
60
+ <span className={cn("font-mono text-xs", className)} {...props}>
61
+ <GitCommitIcon className="mr-1 inline-block size-3" />
62
+ {children}
63
+ </span>
64
+ );
65
+
66
+ export type CommitMessageProps = HTMLAttributes<HTMLSpanElement>;
67
+
68
+ export const CommitMessage = ({
69
+ className,
70
+ children,
71
+ ...props
72
+ }: CommitMessageProps) => (
73
+ <span className={cn("font-medium text-sm", className)} {...props}>
74
+ {children}
75
+ </span>
76
+ );
77
+
78
+ export type CommitMetadataProps = HTMLAttributes<HTMLDivElement>;
79
+
80
+ export const CommitMetadata = ({
81
+ className,
82
+ children,
83
+ ...props
84
+ }: CommitMetadataProps) => (
85
+ <div
86
+ className={cn(
87
+ "flex items-center gap-2 text-muted-foreground text-xs",
88
+ className
89
+ )}
90
+ {...props}
91
+ >
92
+ {children}
93
+ </div>
94
+ );
95
+
96
+ export type CommitSeparatorProps = HTMLAttributes<HTMLSpanElement>;
97
+
98
+ export const CommitSeparator = ({
99
+ className,
100
+ children,
101
+ ...props
102
+ }: CommitSeparatorProps) => (
103
+ <span className={className} {...props}>
104
+ {children ?? "•"}
105
+ </span>
106
+ );
107
+
108
+ export type CommitInfoProps = HTMLAttributes<HTMLDivElement>;
109
+
110
+ export const CommitInfo = ({
111
+ className,
112
+ children,
113
+ ...props
114
+ }: CommitInfoProps) => (
115
+ <div className={cn("flex flex-1 flex-col", className)} {...props}>
116
+ {children}
117
+ </div>
118
+ );
119
+
120
+ export type CommitAuthorProps = HTMLAttributes<HTMLDivElement>;
121
+
122
+ export const CommitAuthor = ({
123
+ className,
124
+ children,
125
+ ...props
126
+ }: CommitAuthorProps) => (
127
+ <div className={cn("flex items-center", className)} {...props}>
128
+ {children}
129
+ </div>
130
+ );
131
+
132
+ export type CommitAuthorAvatarProps = ComponentProps<typeof Avatar> & {
133
+ initials: string;
134
+ };
135
+
136
+ export const CommitAuthorAvatar = ({
137
+ initials,
138
+ className,
139
+ ...props
140
+ }: CommitAuthorAvatarProps) => (
141
+ <Avatar className={cn("size-8", className)} {...props}>
142
+ <AvatarFallback className="text-xs">{initials}</AvatarFallback>
143
+ </Avatar>
144
+ );
145
+
146
+ export type CommitTimestampProps = HTMLAttributes<HTMLTimeElement> & {
147
+ date: Date;
148
+ };
149
+
150
+ const relativeTimeFormat = new Intl.RelativeTimeFormat("en", {
151
+ numeric: "auto",
152
+ });
153
+
154
+ export const CommitTimestamp = ({
155
+ date,
156
+ className,
157
+ children,
158
+ ...props
159
+ }: CommitTimestampProps) => {
160
+ const formatted = relativeTimeFormat.format(
161
+ Math.round((date.getTime() - Date.now()) / (1000 * 60 * 60 * 24)),
162
+ "day"
163
+ );
164
+
165
+ return (
166
+ <time
167
+ className={cn("text-xs", className)}
168
+ dateTime={date.toISOString()}
169
+ {...props}
170
+ >
171
+ {children ?? formatted}
172
+ </time>
173
+ );
174
+ };
175
+
176
+ export type CommitActionsProps = HTMLAttributes<HTMLDivElement>;
177
+
178
+ const handleActionsClick = (e: React.MouseEvent) => e.stopPropagation();
179
+ const handleActionsKeyDown = (e: React.KeyboardEvent) => e.stopPropagation();
180
+
181
+ export const CommitActions = ({
182
+ className,
183
+ children,
184
+ ...props
185
+ }: CommitActionsProps) => (
186
+ // biome-ignore lint/a11y/noNoninteractiveElementInteractions: stopPropagation required for nested interactions
187
+ // biome-ignore lint/a11y/useSemanticElements: fieldset doesn't fit this UI pattern
188
+ <div
189
+ className={cn("flex items-center gap-1", className)}
190
+ onClick={handleActionsClick}
191
+ onKeyDown={handleActionsKeyDown}
192
+ role="group"
193
+ {...props}
194
+ >
195
+ {children}
196
+ </div>
197
+ );
198
+
199
+ export type CommitCopyButtonProps = ComponentProps<typeof Button> & {
200
+ hash: string;
201
+ onCopy?: () => void;
202
+ onError?: (error: Error) => void;
203
+ timeout?: number;
204
+ };
205
+
206
+ export const CommitCopyButton = ({
207
+ hash,
208
+ onCopy,
209
+ onError,
210
+ timeout = 2000,
211
+ children,
212
+ className,
213
+ ...props
214
+ }: CommitCopyButtonProps) => {
215
+ const [isCopied, setIsCopied] = useState(false);
216
+ const timeoutRef = useRef<number>(0);
217
+
218
+ const copyToClipboard = useCallback(async () => {
219
+ if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
220
+ onError?.(new Error("Clipboard API not available"));
221
+ return;
222
+ }
223
+
224
+ try {
225
+ if (!isCopied) {
226
+ await navigator.clipboard.writeText(hash);
227
+ setIsCopied(true);
228
+ onCopy?.();
229
+ timeoutRef.current = window.setTimeout(
230
+ () => setIsCopied(false),
231
+ timeout
232
+ );
233
+ }
234
+ } catch (error) {
235
+ onError?.(error as Error);
236
+ }
237
+ }, [hash, onCopy, onError, timeout, isCopied]);
238
+
239
+ useEffect(
240
+ () => () => {
241
+ window.clearTimeout(timeoutRef.current);
242
+ },
243
+ []
244
+ );
245
+
246
+ const Icon = isCopied ? CheckIcon : CopyIcon;
247
+
248
+ return (
249
+ <Button
250
+ className={cn("size-7 shrink-0", className)}
251
+ onClick={copyToClipboard}
252
+ size="icon"
253
+ variant="ghost"
254
+ {...props}
255
+ >
256
+ {children ?? <Icon size={14} />}
257
+ </Button>
258
+ );
259
+ };
260
+
261
+ export type CommitContentProps = ComponentProps<typeof CollapsibleContent>;
262
+
263
+ export const CommitContent = ({
264
+ className,
265
+ children,
266
+ ...props
267
+ }: CommitContentProps) => (
268
+ <CollapsibleContent className={cn("border-t p-3", className)} {...props}>
269
+ {children}
270
+ </CollapsibleContent>
271
+ );
272
+
273
+ export type CommitFilesProps = HTMLAttributes<HTMLDivElement>;
274
+
275
+ export const CommitFiles = ({
276
+ className,
277
+ children,
278
+ ...props
279
+ }: CommitFilesProps) => (
280
+ <div className={cn("space-y-1", className)} {...props}>
281
+ {children}
282
+ </div>
283
+ );
284
+
285
+ export type CommitFileProps = HTMLAttributes<HTMLDivElement>;
286
+
287
+ export const CommitFile = ({
288
+ className,
289
+ children,
290
+ ...props
291
+ }: CommitFileProps) => (
292
+ <div
293
+ className={cn(
294
+ "flex items-center justify-between gap-2 rounded px-2 py-1 text-sm hover:bg-muted/50",
295
+ className
296
+ )}
297
+ {...props}
298
+ >
299
+ {children}
300
+ </div>
301
+ );
302
+
303
+ export type CommitFileInfoProps = HTMLAttributes<HTMLDivElement>;
304
+
305
+ export const CommitFileInfo = ({
306
+ className,
307
+ children,
308
+ ...props
309
+ }: CommitFileInfoProps) => (
310
+ <div className={cn("flex min-w-0 items-center gap-2", className)} {...props}>
311
+ {children}
312
+ </div>
313
+ );
314
+
315
+ const fileStatusStyles = {
316
+ added: "text-green-600 dark:text-green-400",
317
+ deleted: "text-red-600 dark:text-red-400",
318
+ modified: "text-yellow-600 dark:text-yellow-400",
319
+ renamed: "text-blue-600 dark:text-blue-400",
320
+ };
321
+
322
+ const fileStatusLabels = {
323
+ added: "A",
324
+ deleted: "D",
325
+ modified: "M",
326
+ renamed: "R",
327
+ };
328
+
329
+ export type CommitFileStatusProps = HTMLAttributes<HTMLSpanElement> & {
330
+ status: "added" | "modified" | "deleted" | "renamed";
331
+ };
332
+
333
+ export const CommitFileStatus = ({
334
+ status,
335
+ className,
336
+ children,
337
+ ...props
338
+ }: CommitFileStatusProps) => (
339
+ <span
340
+ className={cn(
341
+ "font-medium font-mono text-xs",
342
+ fileStatusStyles[status],
343
+ className
344
+ )}
345
+ {...props}
346
+ >
347
+ {children ?? fileStatusLabels[status]}
348
+ </span>
349
+ );
350
+
351
+ export type CommitFileIconProps = ComponentProps<typeof FileIcon>;
352
+
353
+ export const CommitFileIcon = ({
354
+ className,
355
+ ...props
356
+ }: CommitFileIconProps) => (
357
+ <FileIcon
358
+ className={cn("size-3.5 shrink-0 text-muted-foreground", className)}
359
+ {...props}
360
+ />
361
+ );
362
+
363
+ export type CommitFilePathProps = HTMLAttributes<HTMLSpanElement>;
364
+
365
+ export const CommitFilePath = ({
366
+ className,
367
+ children,
368
+ ...props
369
+ }: CommitFilePathProps) => (
370
+ <span className={cn("truncate font-mono text-xs", className)} {...props}>
371
+ {children}
372
+ </span>
373
+ );
374
+
375
+ export type CommitFileChangesProps = HTMLAttributes<HTMLDivElement>;
376
+
377
+ export const CommitFileChanges = ({
378
+ className,
379
+ children,
380
+ ...props
381
+ }: CommitFileChangesProps) => (
382
+ <div
383
+ className={cn(
384
+ "flex shrink-0 items-center gap-1 font-mono text-xs",
385
+ className
386
+ )}
387
+ {...props}
388
+ >
389
+ {children}
390
+ </div>
391
+ );
392
+
393
+ export type CommitFileAdditionsProps = HTMLAttributes<HTMLSpanElement> & {
394
+ count: number;
395
+ };
396
+
397
+ export const CommitFileAdditions = ({
398
+ count,
399
+ className,
400
+ children,
401
+ ...props
402
+ }: CommitFileAdditionsProps) => {
403
+ if (count <= 0) {
404
+ return null;
405
+ }
406
+
407
+ return (
408
+ <span
409
+ className={cn("text-green-600 dark:text-green-400", className)}
410
+ {...props}
411
+ >
412
+ {children ?? (
413
+ <>
414
+ <PlusIcon className="inline-block size-3" />
415
+ {count}
416
+ </>
417
+ )}
418
+ </span>
419
+ );
420
+ };
421
+
422
+ export type CommitFileDeletionsProps = HTMLAttributes<HTMLSpanElement> & {
423
+ count: number;
424
+ };
425
+
426
+ export const CommitFileDeletions = ({
427
+ count,
428
+ className,
429
+ children,
430
+ ...props
431
+ }: CommitFileDeletionsProps) => {
432
+ if (count <= 0) {
433
+ return null;
434
+ }
435
+
436
+ return (
437
+ <span
438
+ className={cn("text-red-600 dark:text-red-400", className)}
439
+ {...props}
440
+ >
441
+ {children ?? (
442
+ <>
443
+ <MinusIcon className="inline-block size-3" />
444
+ {count}
445
+ </>
446
+ )}
447
+ </span>
448
+ );
449
+ };
@@ -0,0 +1,173 @@
1
+ "use client";
2
+
3
+ import type { ToolUIPart } from "ai";
4
+ import type { ComponentProps, ReactNode } from "react";
5
+
6
+ import { Alert, AlertDescription } from "@/components/ui/alert";
7
+ import { Button } from "@/components/ui/button";
8
+ import { cn } from "@/lib/utils";
9
+ import { createContext, useContext } from "react";
10
+
11
+ type ToolUIPartApproval =
12
+ | {
13
+ id: string;
14
+ approved?: never;
15
+ reason?: never;
16
+ }
17
+ | {
18
+ id: string;
19
+ approved: boolean;
20
+ reason?: string;
21
+ }
22
+ | {
23
+ id: string;
24
+ approved: true;
25
+ reason?: string;
26
+ }
27
+ | {
28
+ id: string;
29
+ approved: true;
30
+ reason?: string;
31
+ }
32
+ | {
33
+ id: string;
34
+ approved: false;
35
+ reason?: string;
36
+ }
37
+ | undefined;
38
+
39
+ interface ConfirmationContextValue {
40
+ approval: ToolUIPartApproval;
41
+ state: ToolUIPart["state"];
42
+ }
43
+
44
+ const ConfirmationContext = createContext<ConfirmationContextValue | null>(
45
+ null
46
+ );
47
+
48
+ const useConfirmation = () => {
49
+ const context = useContext(ConfirmationContext);
50
+
51
+ if (!context) {
52
+ throw new Error("Confirmation components must be used within Confirmation");
53
+ }
54
+
55
+ return context;
56
+ };
57
+
58
+ export type ConfirmationProps = ComponentProps<typeof Alert> & {
59
+ approval?: ToolUIPartApproval;
60
+ state: ToolUIPart["state"];
61
+ };
62
+
63
+ export const Confirmation = ({
64
+ className,
65
+ approval,
66
+ state,
67
+ ...props
68
+ }: ConfirmationProps) => {
69
+ if (!approval || state === "input-streaming" || state === "input-available") {
70
+ return null;
71
+ }
72
+
73
+ return (
74
+ <ConfirmationContext.Provider value={{ approval, state }}>
75
+ <Alert className={cn("flex flex-col gap-2", className)} {...props} />
76
+ </ConfirmationContext.Provider>
77
+ );
78
+ };
79
+
80
+ export type ConfirmationTitleProps = ComponentProps<typeof AlertDescription>;
81
+
82
+ export const ConfirmationTitle = ({
83
+ className,
84
+ ...props
85
+ }: ConfirmationTitleProps) => (
86
+ <AlertDescription className={cn("inline", className)} {...props} />
87
+ );
88
+
89
+ export interface ConfirmationRequestProps {
90
+ children?: ReactNode;
91
+ }
92
+
93
+ export const ConfirmationRequest = ({ children }: ConfirmationRequestProps) => {
94
+ const { state } = useConfirmation();
95
+
96
+ // Only show when approval is requested
97
+ if (state !== "approval-requested") {
98
+ return null;
99
+ }
100
+
101
+ return children;
102
+ };
103
+
104
+ export interface ConfirmationAcceptedProps {
105
+ children?: ReactNode;
106
+ }
107
+
108
+ export const ConfirmationAccepted = ({
109
+ children,
110
+ }: ConfirmationAcceptedProps) => {
111
+ const { approval, state } = useConfirmation();
112
+
113
+ // Only show when approved and in response states
114
+ if (
115
+ !approval?.approved ||
116
+ (state !== "approval-responded" &&
117
+ state !== "output-denied" &&
118
+ state !== "output-available")
119
+ ) {
120
+ return null;
121
+ }
122
+
123
+ return children;
124
+ };
125
+
126
+ export interface ConfirmationRejectedProps {
127
+ children?: ReactNode;
128
+ }
129
+
130
+ export const ConfirmationRejected = ({
131
+ children,
132
+ }: ConfirmationRejectedProps) => {
133
+ const { approval, state } = useConfirmation();
134
+
135
+ // Only show when rejected and in response states
136
+ if (
137
+ approval?.approved !== false ||
138
+ (state !== "approval-responded" &&
139
+ state !== "output-denied" &&
140
+ state !== "output-available")
141
+ ) {
142
+ return null;
143
+ }
144
+
145
+ return children;
146
+ };
147
+
148
+ export type ConfirmationActionsProps = ComponentProps<"div">;
149
+
150
+ export const ConfirmationActions = ({
151
+ className,
152
+ ...props
153
+ }: ConfirmationActionsProps) => {
154
+ const { state } = useConfirmation();
155
+
156
+ // Only show when approval is requested
157
+ if (state !== "approval-requested") {
158
+ return null;
159
+ }
160
+
161
+ return (
162
+ <div
163
+ className={cn("flex items-center justify-end gap-2 self-end", className)}
164
+ {...props}
165
+ />
166
+ );
167
+ };
168
+
169
+ export type ConfirmationActionProps = ComponentProps<typeof Button>;
170
+
171
+ export const ConfirmationAction = (props: ConfirmationActionProps) => (
172
+ <Button className="h-8 px-3 text-sm" type="button" {...props} />
173
+ );
@@ -0,0 +1,28 @@
1
+ import type { ConnectionLineComponent } from "@xyflow/react";
2
+
3
+ const HALF = 0.5;
4
+
5
+ export const Connection: ConnectionLineComponent = ({
6
+ fromX,
7
+ fromY,
8
+ toX,
9
+ toY,
10
+ }) => (
11
+ <g>
12
+ <path
13
+ className="animated"
14
+ d={`M${fromX},${fromY} C ${fromX + (toX - fromX) * HALF},${fromY} ${fromX + (toX - fromX) * HALF},${toY} ${toX},${toY}`}
15
+ fill="none"
16
+ stroke="var(--color-ring)"
17
+ strokeWidth={1}
18
+ />
19
+ <circle
20
+ cx={toX}
21
+ cy={toY}
22
+ fill="#fff"
23
+ r={3}
24
+ stroke="var(--color-ring)"
25
+ strokeWidth={1}
26
+ />
27
+ </g>
28
+ );