claude-ws 0.3.97 → 0.3.99

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 (86) hide show
  1. package/locales/de.json +374 -12
  2. package/locales/en.json +374 -12
  3. package/locales/es.json +398 -11
  4. package/locales/fr.json +398 -11
  5. package/locales/ja.json +398 -11
  6. package/locales/ko.json +398 -11
  7. package/locales/vi.json +374 -12
  8. package/locales/zh.json +398 -11
  9. package/package.json +1 -1
  10. package/server.ts +283 -6
  11. package/src/app/[locale]/not-found.tsx +6 -3
  12. package/src/app/[locale]/page.tsx +14 -4
  13. package/src/app/api/attempts/[id]/workflow/route.ts +76 -0
  14. package/src/app/api/questions/answer/route.ts +58 -0
  15. package/src/app/api/questions/route.ts +68 -0
  16. package/src/app/api/tasks/[id]/compact/route.ts +62 -0
  17. package/src/components/access-anywhere/api-access-key-setup-modal.tsx +2 -2
  18. package/src/components/access-anywhere/tunnel-settings-dialog.tsx +6 -6
  19. package/src/components/access-anywhere/wizard-step-ctunnel.tsx +8 -8
  20. package/src/components/agent-factory/dependency-tree.tsx +5 -3
  21. package/src/components/agent-factory/discovery-dialog.tsx +26 -22
  22. package/src/components/agent-factory/plugin-detail-dialog.tsx +41 -38
  23. package/src/components/agent-factory/plugin-form-dialog.tsx +23 -20
  24. package/src/components/agent-factory/plugin-list.tsx +20 -17
  25. package/src/components/agent-factory/upload-dialog.tsx +17 -14
  26. package/src/components/auth/agent-provider-dialog.tsx +67 -65
  27. package/src/components/auth/api-key-dialog.tsx +14 -11
  28. package/src/components/auth/auth-error-message.tsx +6 -3
  29. package/src/components/editor/code-editor-with-inline-edit.tsx +4 -2
  30. package/src/components/editor/file-diff-resolver-modal.tsx +31 -26
  31. package/src/components/editor/inline-edit-dialog.tsx +9 -6
  32. package/src/components/editor/selection-mention-popup.tsx +3 -1
  33. package/src/components/header/project-selector.tsx +7 -4
  34. package/src/components/header.tsx +70 -4
  35. package/src/components/kanban/column.tsx +11 -0
  36. package/src/components/kanban/task-card.tsx +70 -4
  37. package/src/components/project-settings/component-selector.tsx +3 -1
  38. package/src/components/project-settings/plugin-upload-dialog.tsx +7 -5
  39. package/src/components/project-settings/project-settings-dialog.tsx +5 -3
  40. package/src/components/questions/questions-panel.tsx +136 -0
  41. package/src/components/settings/folder-browser-dialog.tsx +29 -25
  42. package/src/components/settings/settings-page.tsx +64 -18
  43. package/src/components/settings/setup-dialog.tsx +26 -23
  44. package/src/components/setup/unified-setup-wizard.tsx +12 -9
  45. package/src/components/sidebar/file-browser/file-create-buttons.tsx +7 -3
  46. package/src/components/sidebar/file-browser/file-tab-content.tsx +19 -15
  47. package/src/components/sidebar/file-browser/file-tabs-panel.tsx +7 -4
  48. package/src/components/sidebar/file-browser/file-tree.tsx +3 -1
  49. package/src/components/sidebar/git-changes/branch-checkout-modal.tsx +6 -4
  50. package/src/components/sidebar/git-changes/commit-details-modal.tsx +5 -3
  51. package/src/components/sidebar/git-changes/diff-tabs-panel.tsx +3 -1
  52. package/src/components/sidebar/git-changes/git-file-item.tsx +8 -6
  53. package/src/components/sidebar/git-changes/git-graph.tsx +8 -5
  54. package/src/components/sidebar/git-changes/git-panel.tsx +28 -27
  55. package/src/components/sidebar/git-changes/git-section.tsx +5 -3
  56. package/src/components/sidebar/shells/shell-panel.tsx +3 -1
  57. package/src/components/task/attachment-bar.tsx +4 -1
  58. package/src/components/task/attempt-item.tsx +7 -5
  59. package/src/components/task/conversation-view.tsx +21 -13
  60. package/src/components/task/floating-chat-window.tsx +14 -5
  61. package/src/components/task/interactive-command/checkpoint-list.tsx +5 -3
  62. package/src/components/task/interactive-command/confirm-dialog.tsx +9 -4
  63. package/src/components/task/interactive-command/interactive-command-overlay.tsx +23 -9
  64. package/src/components/task/interactive-command/question-prompt.tsx +12 -8
  65. package/src/components/task/pending-question-indicator.tsx +5 -3
  66. package/src/components/task/prompt-input.tsx +1 -1
  67. package/src/components/task/shell-log-view.tsx +3 -1
  68. package/src/components/task/status-line.tsx +84 -23
  69. package/src/components/task/task-detail-panel.tsx +27 -27
  70. package/src/components/task/task-shell-indicator.tsx +10 -6
  71. package/src/components/terminal/terminal-context-menu.tsx +6 -4
  72. package/src/components/terminal/terminal-instance.tsx +11 -3
  73. package/src/components/terminal/terminal-panel.tsx +6 -3
  74. package/src/components/terminal/terminal-shortcut-bar.tsx +3 -1
  75. package/src/components/terminal/terminal-tab-bar.tsx +5 -3
  76. package/src/components/workflow/workflow-panel.tsx +181 -0
  77. package/src/hooks/use-attempt-stream.ts +96 -3
  78. package/src/lib/agent-manager.ts +89 -3
  79. package/src/lib/db/index.ts +18 -0
  80. package/src/lib/db/schema.ts +29 -0
  81. package/src/lib/process-manager.ts +28 -7
  82. package/src/lib/session-manager.ts +60 -0
  83. package/src/lib/usage-tracker.ts +19 -19
  84. package/src/lib/workflow-tracker.ts +118 -20
  85. package/src/stores/questions-store.ts +76 -0
  86. package/src/stores/workflow-store.ts +71 -0
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useEffect, useRef } from 'react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import {
5
6
  Dialog,
6
7
  DialogContent,
@@ -80,6 +81,8 @@ export function PluginDetailDialog({
80
81
  open,
81
82
  onOpenChange,
82
83
  }: PluginDetailDialogProps) {
84
+ const t = useTranslations('agentFactory');
85
+ const tCommon = useTranslations('common');
83
86
  const isImported = isImportedPlugin(plugin);
84
87
  const [activeTab, setActiveTab] = useState<'details' | 'files' | 'dependencies'>('details');
85
88
  const [files, setFiles] = useState<FileNode[]>([]);
@@ -170,7 +173,7 @@ export function PluginDetailDialog({
170
173
  let fileData;
171
174
  if (isImported) {
172
175
  const res = await fetch(`/api/agent-factory/plugins/${plugin.id}/files`);
173
- if (!res.ok) throw new Error('Failed to load files');
176
+ if (!res.ok) throw new Error(t('failedToLoadFiles'));
174
177
  fileData = await res.json();
175
178
  } else {
176
179
  // For discovered plugins, use a different endpoint that reads from sourcePath
@@ -179,12 +182,12 @@ export function PluginDetailDialog({
179
182
  headers: { 'Content-Type': 'application/json' },
180
183
  body: JSON.stringify({ sourcePath: plugin.sourcePath, type: plugin.type }),
181
184
  });
182
- if (!res.ok) throw new Error('Failed to load files');
185
+ if (!res.ok) throw new Error(t('failedToLoadFiles'));
183
186
  fileData = await res.json();
184
187
  }
185
188
  setFiles(fileData.files || []);
186
189
  } catch (err) {
187
- setError(err instanceof Error ? err.message : 'Failed to load files');
190
+ setError(err instanceof Error ? err.message : t('failedToLoadFiles'));
188
191
  } finally {
189
192
  setLoadingFiles(false);
190
193
  }
@@ -198,7 +201,7 @@ export function PluginDetailDialog({
198
201
  if (isImported) {
199
202
  const encodedPath = filePath.split('/').map(encodeURIComponent).join('/');
200
203
  const res = await fetch(`/api/agent-factory/plugins/${plugin.id}/files/${encodedPath}`);
201
- if (!res.ok) throw new Error('Failed to load file');
204
+ if (!res.ok) throw new Error(t('failedToLoadFile'));
202
205
  data = await res.json();
203
206
  } else {
204
207
  // For discovered plugins
@@ -210,13 +213,13 @@ export function PluginDetailDialog({
210
213
  filePath,
211
214
  }),
212
215
  });
213
- if (!res.ok) throw new Error('Failed to load file');
216
+ if (!res.ok) throw new Error(t('failedToLoadFile'));
214
217
  data = await res.json();
215
218
  }
216
219
  setFileContent(data);
217
220
  setMobileFileModalOpen(true);
218
221
  } catch (err) {
219
- setError(err instanceof Error ? err.message : 'Failed to load file');
222
+ setError(err instanceof Error ? err.message : t('failedToLoadFile'));
220
223
  } finally {
221
224
  setLoadingContent(false);
222
225
  }
@@ -229,7 +232,7 @@ export function PluginDetailDialog({
229
232
  let data;
230
233
  if (isImported) {
231
234
  const res = await fetch(`/api/agent-factory/plugins/${plugin.id}/dependencies`);
232
- if (!res.ok) throw new Error('Failed to load dependencies');
235
+ if (!res.ok) throw new Error(t('failedToLoadDependencies'));
233
236
  data = await res.json();
234
237
  } else {
235
238
  // For discovered plugins
@@ -238,12 +241,12 @@ export function PluginDetailDialog({
238
241
  headers: { 'Content-Type': 'application/json' },
239
242
  body: JSON.stringify({ sourcePath: plugin.sourcePath, type: plugin.type }),
240
243
  });
241
- if (!res.ok) throw new Error('Failed to load dependencies');
244
+ if (!res.ok) throw new Error(t('failedToLoadDependencies'));
242
245
  data = await res.json();
243
246
  }
244
247
  setDependencies(data);
245
248
  } catch (err) {
246
- setError(err instanceof Error ? err.message : 'Failed to load dependencies');
249
+ setError(err instanceof Error ? err.message : t('failedToLoadDependencies'));
247
250
  } finally {
248
251
  setLoadingDeps(false);
249
252
  }
@@ -260,7 +263,7 @@ export function PluginDetailDialog({
260
263
  headers: { 'Content-Type': 'application/json' },
261
264
  body: JSON.stringify({ useClaude: true }),
262
265
  });
263
- if (!res.ok) throw new Error('Failed to re-resolve dependencies');
266
+ if (!res.ok) throw new Error(t('failedToReResolveDependencies'));
264
267
  const data = await res.json();
265
268
  setDependencies(data);
266
269
  } else {
@@ -274,12 +277,12 @@ export function PluginDetailDialog({
274
277
  useClaude: true,
275
278
  }),
276
279
  });
277
- if (!res.ok) throw new Error('Failed to analyze dependencies');
280
+ if (!res.ok) throw new Error(t('failedToAnalyzeDependencies'));
278
281
  const data = await res.json();
279
282
  setDependencies(data);
280
283
  }
281
284
  } catch (err) {
282
- setError(err instanceof Error ? err.message : 'Failed to re-resolve dependencies');
285
+ setError(err instanceof Error ? err.message : t('failedToReResolveDependencies'));
283
286
  } finally {
284
287
  setReResolvingDeps(false);
285
288
  }
@@ -466,19 +469,19 @@ export function PluginDetailDialog({
466
469
  <Package className="w-6 h-6" />
467
470
  {plugin.name}
468
471
  {!isImported && (
469
- <Badge variant="outline" className="text-xs">Discovered</Badge>
472
+ <Badge variant="outline" className="text-xs">{t('discovered')}</Badge>
470
473
  )}
471
474
  </DialogTitle>
472
475
  <DialogDescription>
473
- {isImported ? 'Plugin details and files' : 'Discovered plugin details and files'}
476
+ {isImported ? t('pluginDetails') : t('discoveredPluginDetails')}
474
477
  </DialogDescription>
475
478
  </DialogHeader>
476
479
 
477
480
  <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as 'details' | 'files' | 'dependencies')} className="flex-1 flex flex-col overflow-hidden">
478
481
  <TabsList>
479
- <TabsTrigger value="details">Details</TabsTrigger>
480
- <TabsTrigger value="files">Files</TabsTrigger>
481
- <TabsTrigger value="dependencies">Dependencies</TabsTrigger>
482
+ <TabsTrigger value="details">{t('details')}</TabsTrigger>
483
+ <TabsTrigger value="files">{t('files')}</TabsTrigger>
484
+ <TabsTrigger value="dependencies">{t('dependencies')}</TabsTrigger>
482
485
  </TabsList>
483
486
 
484
487
  <TabsContent value="details" className="flex-1 overflow-y-auto mt-4">
@@ -496,7 +499,7 @@ export function PluginDetailDialog({
496
499
  <div>
497
500
  <div className="flex items-center gap-2 mb-2">
498
501
  <FileText className="w-4 h-4 text-muted-foreground" />
499
- <span className="text-sm font-medium">Description</span>
502
+ <span className="text-sm font-medium">{t('pluginDescription')}</span>
500
503
  </div>
501
504
  <p className="text-sm text-muted-foreground pl-6">
502
505
  {plugin.description}
@@ -508,7 +511,7 @@ export function PluginDetailDialog({
508
511
  <div>
509
512
  <div className="flex items-center gap-2 mb-2">
510
513
  <Folder className="w-4 h-4 text-muted-foreground" />
511
- <span className="text-sm font-medium">Source Path</span>
514
+ <span className="text-sm font-medium">{t('sourcePath')}</span>
512
515
  </div>
513
516
  <code className="text-xs bg-muted px-2 py-1 rounded block pl-6 break-all">
514
517
  {plugin.sourcePath}
@@ -518,7 +521,7 @@ export function PluginDetailDialog({
518
521
  {/* Storage Type - only for imported */}
519
522
  {isImported && (
520
523
  <div className="flex items-center gap-2">
521
- <span className="text-sm text-muted-foreground">Storage:</span>
524
+ <span className="text-sm text-muted-foreground">{t('storage')}</span>
522
525
  <Badge variant="secondary">{plugin.storageType}</Badge>
523
526
  </div>
524
527
  )}
@@ -528,7 +531,7 @@ export function PluginDetailDialog({
528
531
  <div>
529
532
  <div className="flex items-center gap-2 mb-2">
530
533
  <FileText className="w-4 h-4 text-muted-foreground" />
531
- <span className="text-sm font-medium">Metadata</span>
534
+ <span className="text-sm font-medium">{t('metadata')}</span>
532
535
  </div>
533
536
  <pre className="text-xs bg-muted p-3 rounded overflow-x-auto pl-6">
534
537
  {metadataStr}
@@ -555,7 +558,7 @@ export function PluginDetailDialog({
555
558
  {/* File Tree */}
556
559
  <div className="border rounded-lg overflow-hidden">
557
560
  <div className="p-2 border-b bg-muted/50 text-sm font-medium">
558
- Files
561
+ {t('files')}
559
562
  </div>
560
563
  <div className="p-2 max-h-[400px] overflow-y-auto">
561
564
  {loadingFiles ? (
@@ -565,7 +568,7 @@ export function PluginDetailDialog({
565
568
  ) : error ? (
566
569
  <div className="text-sm text-destructive py-4">{error}</div>
567
570
  ) : files.length === 0 ? (
568
- <div className="text-sm text-muted-foreground py-4">No files found</div>
571
+ <div className="text-sm text-muted-foreground py-4">{t('noFilesFound')}</div>
569
572
  ) : (
570
573
  renderFileTree(files)
571
574
  )}
@@ -580,7 +583,7 @@ export function PluginDetailDialog({
580
583
  <div className="text-sm text-muted-foreground">
581
584
  {dependencies && dependencies.resolvedAt
582
585
  ? `Last resolved: ${new Date(dependencies.resolvedAt).toLocaleString()}`
583
- : 'Dependencies'}
586
+ : t('dependencies')}
584
587
  </div>
585
588
  <Button
586
589
  size="sm"
@@ -592,12 +595,12 @@ export function PluginDetailDialog({
592
595
  {reResolvingDeps ? (
593
596
  <>
594
597
  <Loader2 className="w-3 h-3 animate-spin" />
595
- Re-resolving...
598
+ {t('reResolving')}
596
599
  </>
597
600
  ) : (
598
601
  <>
599
602
  <RefreshCw className="w-3 h-3" />
600
- Re-resolve
603
+ {t('reResolve')}
601
604
  </>
602
605
  )}
603
606
  </Button>
@@ -610,18 +613,18 @@ export function PluginDetailDialog({
610
613
  ) : error ? (
611
614
  <div className="text-sm text-destructive py-4">{error}</div>
612
615
  ) : !dependencies ? (
613
- <div className="text-sm text-muted-foreground py-4">No dependencies found</div>
616
+ <div className="text-sm text-muted-foreground py-4">{t('noDependenciesFound')}</div>
614
617
  ) : (
615
618
  <>
616
619
  {/* Library Dependencies */}
617
620
  <div>
618
621
  <div className="flex items-center gap-2 mb-3">
619
622
  <PackageSearch className="w-4 h-4 text-muted-foreground" />
620
- <h3 className="text-sm font-medium">Library Dependencies</h3>
623
+ <h3 className="text-sm font-medium">{t('libraryDependencies')}</h3>
621
624
  <Badge variant="secondary">{dependencies.libraries.length}</Badge>
622
625
  </div>
623
626
  {dependencies.libraries.length === 0 ? (
624
- <p className="text-sm text-muted-foreground pl-6">No external libraries found</p>
627
+ <p className="text-sm text-muted-foreground pl-6">{t('noExternalLibraries')}</p>
625
628
  ) : (
626
629
  <div className="pl-6 space-y-4">
627
630
  {/* Library badges */}
@@ -642,7 +645,7 @@ export function PluginDetailDialog({
642
645
  <div className="mt-4">
643
646
  <div className="flex items-center gap-2 mb-2">
644
647
  <Terminal className="w-4 h-4 text-muted-foreground" />
645
- <h4 className="text-sm font-medium">Install Scripts</h4>
648
+ <h4 className="text-sm font-medium">{t('installScripts')}</h4>
646
649
  </div>
647
650
 
648
651
  <div className="border rounded-lg overflow-hidden">
@@ -790,13 +793,13 @@ export function PluginDetailDialog({
790
793
  <div>
791
794
  <div className="flex items-center gap-2 mb-3">
792
795
  <Package className="w-4 h-4 text-muted-foreground" />
793
- <h3 className="text-sm font-medium">Plugin Dependencies</h3>
796
+ <h3 className="text-sm font-medium">{t('pluginDependencies')}</h3>
794
797
  <Badge variant="secondary">
795
798
  {dependencies.dependencyTree ? countPlugins(dependencies.dependencyTree) : dependencies.plugins.length}
796
799
  </Badge>
797
800
  </div>
798
801
  {(!dependencies.dependencyTree || dependencies.dependencyTree.length === 0) && dependencies.plugins.length === 0 ? (
799
- <p className="text-sm text-muted-foreground pl-6">No plugin dependencies found</p>
802
+ <p className="text-sm text-muted-foreground pl-6">{t('noPluginDependencies')}</p>
800
803
  ) : (
801
804
  <div className="pl-6">
802
805
  {dependencies.dependencyTree ? (
@@ -824,7 +827,7 @@ export function PluginDetailDialog({
824
827
  {dependencies.hasCycles && (
825
828
  <div className="text-orange-500">
826
829
  <AlertTriangle className="w-3 h-3 inline mr-1" />
827
- Circular dependencies detected
830
+ {t('circularDependencies')}
828
831
  </div>
829
832
  )}
830
833
  {dependencies.resolvedAt && (
@@ -839,7 +842,7 @@ export function PluginDetailDialog({
839
842
  </Tabs>
840
843
 
841
844
  <div className="flex justify-end pt-4 border-t">
842
- <Button onClick={() => onOpenChange(false)}>Close</Button>
845
+ <Button onClick={() => onOpenChange(false)}>{tCommon('close')}</Button>
843
846
  </div>
844
847
  </DialogContent>
845
848
  </Dialog>
@@ -868,7 +871,7 @@ export function PluginDetailDialog({
868
871
  onClick={handleStartEdit}
869
872
  >
870
873
  <Edit3 className="w-3 h-3" />
871
- Edit
874
+ {tCommon('edit')}
872
875
  </Button>
873
876
  )}
874
877
  <Button
@@ -925,7 +928,7 @@ export function PluginDetailDialog({
925
928
  disabled={saving}
926
929
  >
927
930
  <XIcon className="w-3 h-3 mr-1" />
928
- Cancel
931
+ {tCommon('cancel')}
929
932
  </Button>
930
933
  <Button
931
934
  size="sm"
@@ -935,12 +938,12 @@ export function PluginDetailDialog({
935
938
  {saving ? (
936
939
  <>
937
940
  <Loader2 className="w-3 h-3 mr-1 animate-spin" />
938
- Saving...
941
+ {tCommon('saving')}
939
942
  </>
940
943
  ) : (
941
944
  <>
942
945
  <Save className="w-3 h-3 mr-1" />
943
- Save
946
+ {tCommon('save')}
944
947
  </>
945
948
  )}
946
949
  </Button>
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useEffect, useMemo } from 'react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import {
5
6
  Dialog,
6
7
  DialogContent,
@@ -31,6 +32,8 @@ export function PluginFormDialog({
31
32
  onOpenChange,
32
33
  plugin,
33
34
  }: PluginFormDialogProps) {
35
+ const t = useTranslations('agentFactory');
36
+ const tCommon = useTranslations('common');
34
37
  const { createPlugin, updatePlugin, error } = useAgentFactoryStore();
35
38
  const [isSubmitting, setIsSubmitting] = useState(false);
36
39
 
@@ -110,12 +113,12 @@ export function PluginFormDialog({
110
113
  <DialogContent className="sm:max-w-[500px]">
111
114
  <DialogHeader>
112
115
  <DialogTitle>
113
- {plugin ? 'Edit Plugin' : 'Create New Plugin'}
116
+ {plugin ? t('editPlugin') : t('createNewPlugin')}
114
117
  </DialogTitle>
115
118
  <DialogDescription>
116
119
  {plugin
117
- ? 'Update the plugin details below.'
118
- : 'Add a new plugin to the Agent Factory registry.'}
120
+ ? t('updatePluginDescription')
121
+ : t('addPluginDescription')}
119
122
  </DialogDescription>
120
123
  </DialogHeader>
121
124
 
@@ -123,27 +126,27 @@ export function PluginFormDialog({
123
126
  {/* Plugin Type (only for new plugins) */}
124
127
  {!plugin && (
125
128
  <div className="space-y-2">
126
- <label className="text-sm font-medium">Type</label>
129
+ <label className="text-sm font-medium">{t('type')}</label>
127
130
  <select
128
131
  value={type}
129
132
  onChange={(e) => setType(e.target.value as 'skill' | 'command' | 'agent')}
130
133
  className="w-full border rounded-md p-2 bg-background"
131
134
  disabled={isSubmitting}
132
135
  >
133
- <option value="skill">Skill</option>
134
- <option value="command">Command</option>
135
- <option value="agent">Agent</option>
136
+ <option value="skill">{t('skill')}</option>
137
+ <option value="command">{t('command')}</option>
138
+ <option value="agent">{t('agent')}</option>
136
139
  </select>
137
140
  </div>
138
141
  )}
139
142
 
140
143
  {/* Name */}
141
144
  <div className="space-y-2">
142
- <label className="text-sm font-medium">Name *</label>
145
+ <label className="text-sm font-medium">{t('name')} *</label>
143
146
  <Input
144
147
  value={name}
145
148
  onChange={(e) => setName(e.target.value)}
146
- placeholder="Plugin name"
149
+ placeholder={t('pluginName')}
147
150
  disabled={isSubmitting}
148
151
  required
149
152
  />
@@ -151,11 +154,11 @@ export function PluginFormDialog({
151
154
 
152
155
  {/* Description */}
153
156
  <div className="space-y-2">
154
- <label className="text-sm font-medium">Description</label>
157
+ <label className="text-sm font-medium">{t('pluginDescription')}</label>
155
158
  <Input
156
159
  value={description}
157
160
  onChange={(e) => setDescription(e.target.value)}
158
- placeholder="Brief description of the plugin"
161
+ placeholder={t('pluginDescription')}
159
162
  disabled={isSubmitting}
160
163
  />
161
164
  </div>
@@ -163,7 +166,7 @@ export function PluginFormDialog({
163
166
  {/* Source Path - show for editing, read-only preview for new */}
164
167
  {plugin ? (
165
168
  <div className="space-y-2">
166
- <label className="text-sm font-medium">Source Path *</label>
169
+ <label className="text-sm font-medium">{t('sourcePath')} *</label>
167
170
  <Input
168
171
  value={sourcePath}
169
172
  onChange={(e) => setSourcePath(e.target.value)}
@@ -174,27 +177,27 @@ export function PluginFormDialog({
174
177
  </div>
175
178
  ) : (
176
179
  <div className="space-y-2">
177
- <label className="text-sm font-medium">Source Path</label>
180
+ <label className="text-sm font-medium">{t('sourcePath')}</label>
178
181
  <div className="text-sm text-muted-foreground bg-muted/50 p-2 rounded border">
179
182
  {previewPath}
180
183
  </div>
181
- <p className="text-xs text-muted-foreground">Path will be auto-generated based on plugin type and name</p>
184
+ <p className="text-xs text-muted-foreground">{t('pathAutoGenerated')}</p>
182
185
  </div>
183
186
  )}
184
187
 
185
188
  {/* Storage Type (only for new plugins) */}
186
189
  {!plugin && (
187
190
  <div className="space-y-2">
188
- <label className="text-sm font-medium">Storage Type</label>
191
+ <label className="text-sm font-medium">{t('storageType')}</label>
189
192
  <select
190
193
  value={storageType}
191
194
  onChange={(e) => setStorageType(e.target.value as 'local' | 'imported' | 'external')}
192
195
  className="w-full border rounded-md p-2 bg-background"
193
196
  disabled={isSubmitting}
194
197
  >
195
- <option value="local">Local</option>
196
- <option value="imported">Imported</option>
197
- <option value="external">External</option>
198
+ <option value="local">{t('local')}</option>
199
+ <option value="imported">{t('imported')}</option>
200
+ <option value="external">{t('external')}</option>
198
201
  </select>
199
202
  </div>
200
203
  )}
@@ -214,10 +217,10 @@ export function PluginFormDialog({
214
217
  onClick={() => onOpenChange(false)}
215
218
  disabled={isSubmitting}
216
219
  >
217
- Cancel
220
+ {tCommon('cancel')}
218
221
  </Button>
219
222
  <Button type="submit" disabled={isSubmitting || !name.trim()}>
220
- {isSubmitting ? 'Saving...' : plugin ? 'Update' : 'Create'}
223
+ {isSubmitting ? tCommon('saving') : plugin ? t('update') : tCommon('create')}
221
224
  </Button>
222
225
  </div>
223
226
  </form>
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useEffect, useState } from 'react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import { Package, Plus, RefreshCw, Search, Trash2, Edit, X, Upload } from 'lucide-react';
5
6
  import { useAgentFactoryStore } from '@/stores/agent-factory-store';
6
7
  import { useAgentFactoryUIStore } from '@/stores/agent-factory-ui-store';
@@ -13,6 +14,8 @@ import { DiscoveryDialog } from './discovery-dialog';
13
14
  import { UploadDialog } from './upload-dialog';
14
15
 
15
16
  export function PluginList() {
17
+ const t = useTranslations('agentFactory');
18
+ const tCommon = useTranslations('common');
16
19
  const { plugins, loading, error, fetchPlugins, deletePlugin } = useAgentFactoryStore();
17
20
  const { setOpen: setAgentFactoryOpen } = useAgentFactoryUIStore();
18
21
  const [filter, setFilter] = useState<'all' | 'skill' | 'command' | 'agent' | 'agent_set'>('all');
@@ -43,7 +46,7 @@ export function PluginList() {
43
46
  });
44
47
 
45
48
  const handleDelete = async (id: string) => {
46
- if (!confirm('Are you sure you want to delete this plugin?')) return;
49
+ if (!confirm(t('deletePluginConfirm'))) return;
47
50
  try {
48
51
  await deletePlugin(id);
49
52
  } catch (error) {
@@ -82,7 +85,7 @@ export function PluginList() {
82
85
  <div className="flex items-center justify-between sm:justify-normal gap-3">
83
86
  <div className="flex items-center gap-3">
84
87
  <Package className="w-6 h-6" />
85
- <h1 className="text-2xl font-bold">Agent Factory</h1>
88
+ <h1 className="text-2xl font-bold">{t('title')}</h1>
86
89
  </div>
87
90
  <Button variant="ghost" size="icon" className="sm:hidden" onClick={() => setAgentFactoryOpen(false)}>
88
91
  <X className="w-5 h-5" />
@@ -91,19 +94,19 @@ export function PluginList() {
91
94
  <div className="flex flex-wrap gap-2 justify-end">
92
95
  <Button variant="outline" size="sm" onClick={() => fetchPlugins()}>
93
96
  <RefreshCw className="w-4 h-4 mr-2" />
94
- Refresh
97
+ {tCommon('refresh')}
95
98
  </Button>
96
99
  <Button variant="outline" size="sm" onClick={() => setDiscoveryOpen(true)}>
97
100
  <Package className="w-4 h-4 mr-2" />
98
- Discover
101
+ {tCommon('discover')}
99
102
  </Button>
100
103
  <Button variant="outline" size="sm" onClick={() => setUploadOpen(true)}>
101
104
  <Upload className="w-4 h-4 mr-2" />
102
- Upload
105
+ {tCommon('upload')}
103
106
  </Button>
104
107
  <Button size="sm" onClick={() => setCreateFormOpen(true)}>
105
108
  <Plus className="w-4 h-4 mr-2" />
106
- New
109
+ {tCommon('new')}
107
110
  </Button>
108
111
  <Button variant="ghost" size="icon" className="hidden sm:flex" onClick={() => setAgentFactoryOpen(false)}>
109
112
  <X className="w-5 h-5" />
@@ -113,7 +116,7 @@ export function PluginList() {
113
116
 
114
117
  {/* Description */}
115
118
  <p className="text-sm text-muted-foreground mb-6">
116
- This is your plugin pool. To install plugins to a specific project, go to <strong>Project Settings</strong> and select plugins to install.
119
+ {t('description')}
117
120
  </p>
118
121
 
119
122
  {/* Filters and Search */}
@@ -129,14 +132,14 @@ export function PluginList() {
129
132
  : 'bg-secondary text-secondary-foreground hover:bg-secondary/80'
130
133
  }`}
131
134
  >
132
- {type === 'all' ? 'All' : type === 'agent_set' ? 'Agent Sets' : `${type}s`}
135
+ {type === 'all' ? t('all') : type === 'agent_set' ? t('agentSets') : type === 'skill' ? t('skills') : type === 'command' ? t('commands') : t('agents')}
133
136
  </button>
134
137
  ))}
135
138
  </div>
136
139
  <div className="flex-1 relative">
137
140
  <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
138
141
  <Input
139
- placeholder="Search plugins..."
142
+ placeholder={t('searchPlugins')}
140
143
  value={searchQuery}
141
144
  onChange={(e) => setSearchQuery(e.target.value)}
142
145
  className="pl-10"
@@ -154,7 +157,7 @@ export function PluginList() {
154
157
  {/* Loading */}
155
158
  {loading && (
156
159
  <div className="text-center py-12 text-muted-foreground">
157
- Loading plugins...
160
+ {t('loadingPlugins')}
158
161
  </div>
159
162
  )}
160
163
 
@@ -178,7 +181,7 @@ export function PluginList() {
178
181
  </span>
179
182
  </div>
180
183
  <p className="text-sm text-muted-foreground line-clamp-2 mb-4">
181
- {plugin.description || 'No description'}
184
+ {plugin.description || t('noDescription')}
182
185
  </p>
183
186
  <div className="flex items-center gap-2 text-xs text-muted-foreground">
184
187
  <span className="capitalize">{plugin.storageType}</span>
@@ -215,25 +218,25 @@ export function PluginList() {
215
218
  {!loading && filteredPlugins.length === 0 && (
216
219
  <div className="text-center py-12">
217
220
  <Package className="w-12 h-12 mx-auto mb-4 text-muted-foreground" />
218
- <h3 className="text-lg font-semibold mb-2">No plugins found</h3>
221
+ <h3 className="text-lg font-semibold mb-2">{t('noPluginsFound')}</h3>
219
222
  <p className="text-muted-foreground mb-4">
220
223
  {searchQuery || filter !== 'all'
221
- ? 'Try adjusting your filters or search query'
222
- : 'Get started by discovering existing plugins or creating a new one'}
224
+ ? t('adjustFilters')
225
+ : t('getStartedDiscover')}
223
226
  </p>
224
227
  {!searchQuery && filter === 'all' && (
225
228
  <div className="flex justify-center gap-2">
226
229
  <Button variant="outline" onClick={() => setDiscoveryOpen(true)}>
227
230
  <Package className="w-4 h-4 mr-2" />
228
- Discover Plugins
231
+ {tCommon('discover')}
229
232
  </Button>
230
233
  <Button variant="outline" onClick={() => setUploadOpen(true)}>
231
234
  <Upload className="w-4 h-4 mr-2" />
232
- Upload
235
+ {tCommon('upload')}
233
236
  </Button>
234
237
  <Button onClick={() => setCreateFormOpen(true)}>
235
238
  <Plus className="w-4 h-4 mr-2" />
236
- New
239
+ {tCommon('new')}
237
240
  </Button>
238
241
  </div>
239
242
  )}