@geminilight/mindos 0.3.0 → 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 (80) hide show
  1. package/app/app/api/mcp/agents/route.ts +72 -0
  2. package/app/app/api/mcp/install/route.ts +95 -0
  3. package/app/app/api/mcp/status/route.ts +47 -0
  4. package/app/app/api/setup/check-port/route.ts +41 -0
  5. package/app/app/api/skills/route.ts +208 -0
  6. package/app/app/api/sync/route.ts +54 -3
  7. package/app/app/api/update-check/route.ts +52 -0
  8. package/app/app/globals.css +12 -0
  9. package/app/app/layout.tsx +4 -2
  10. package/app/app/login/page.tsx +20 -13
  11. package/app/app/page.tsx +19 -2
  12. package/app/app/setup/page.tsx +2 -0
  13. package/app/app/view/[...path]/ViewPageClient.tsx +47 -21
  14. package/app/app/view/[...path]/loading.tsx +1 -1
  15. package/app/app/view/[...path]/not-found.tsx +101 -0
  16. package/app/components/AskFab.tsx +1 -1
  17. package/app/components/AskModal.tsx +1 -1
  18. package/app/components/Backlinks.tsx +1 -1
  19. package/app/components/Breadcrumb.tsx +13 -3
  20. package/app/components/CsvView.tsx +5 -6
  21. package/app/components/DirView.tsx +42 -21
  22. package/app/components/FindInPage.tsx +211 -0
  23. package/app/components/HomeContent.tsx +97 -44
  24. package/app/components/JsonView.tsx +1 -2
  25. package/app/components/MarkdownEditor.tsx +1 -2
  26. package/app/components/OnboardingView.tsx +6 -7
  27. package/app/components/SettingsModal.tsx +5 -2
  28. package/app/components/SetupWizard.tsx +499 -172
  29. package/app/components/Sidebar.tsx +1 -1
  30. package/app/components/UpdateBanner.tsx +101 -0
  31. package/app/components/renderers/{AgentInspectorRenderer.tsx → agent-inspector/AgentInspectorRenderer.tsx} +13 -11
  32. package/app/components/renderers/agent-inspector/manifest.ts +14 -0
  33. package/app/components/renderers/{BacklinksRenderer.tsx → backlinks/BacklinksRenderer.tsx} +6 -6
  34. package/app/components/renderers/backlinks/manifest.ts +14 -0
  35. package/app/components/renderers/config/manifest.ts +14 -0
  36. package/app/components/renderers/csv/BoardView.tsx +12 -12
  37. package/app/components/renderers/csv/ConfigPanel.tsx +7 -8
  38. package/app/components/renderers/{CsvRenderer.tsx → csv/CsvRenderer.tsx} +8 -9
  39. package/app/components/renderers/csv/GalleryView.tsx +3 -3
  40. package/app/components/renderers/csv/TableView.tsx +4 -5
  41. package/app/components/renderers/csv/manifest.ts +14 -0
  42. package/app/components/renderers/{DiffRenderer.tsx → diff/DiffRenderer.tsx} +10 -9
  43. package/app/components/renderers/diff/manifest.ts +14 -0
  44. package/app/components/renderers/{GraphRenderer.tsx → graph/GraphRenderer.tsx} +4 -5
  45. package/app/components/renderers/graph/manifest.ts +14 -0
  46. package/app/components/renderers/{SummaryRenderer.tsx → summary/SummaryRenderer.tsx} +6 -6
  47. package/app/components/renderers/summary/manifest.ts +14 -0
  48. package/app/components/renderers/{TimelineRenderer.tsx → timeline/TimelineRenderer.tsx} +6 -6
  49. package/app/components/renderers/timeline/manifest.ts +14 -0
  50. package/app/components/renderers/{TodoRenderer.tsx → todo/TodoRenderer.tsx} +2 -2
  51. package/app/components/renderers/todo/manifest.ts +14 -0
  52. package/app/components/renderers/{WorkflowRenderer.tsx → workflow/WorkflowRenderer.tsx} +13 -13
  53. package/app/components/renderers/workflow/manifest.ts +14 -0
  54. package/app/components/settings/McpTab.tsx +549 -0
  55. package/app/components/settings/SyncTab.tsx +139 -50
  56. package/app/components/settings/types.ts +1 -1
  57. package/app/data/pages/home.png +0 -0
  58. package/app/lib/i18n.ts +226 -19
  59. package/app/lib/renderers/index.ts +20 -89
  60. package/app/lib/renderers/registry.ts +4 -1
  61. package/app/lib/settings.ts +3 -0
  62. package/app/package.json +1 -0
  63. package/app/types/semver.d.ts +8 -0
  64. package/bin/cli.js +137 -24
  65. package/bin/lib/build.js +53 -18
  66. package/bin/lib/colors.js +3 -1
  67. package/bin/lib/config.js +4 -0
  68. package/bin/lib/constants.js +2 -0
  69. package/bin/lib/debug.js +10 -0
  70. package/bin/lib/mcp-install.js +4 -1
  71. package/bin/lib/port.js +8 -2
  72. package/bin/lib/startup.js +21 -20
  73. package/bin/lib/stop.js +41 -3
  74. package/bin/lib/sync.js +65 -53
  75. package/bin/lib/update-check.js +94 -0
  76. package/bin/lib/utils.js +2 -2
  77. package/package.json +1 -1
  78. package/scripts/gen-renderer-index.js +57 -0
  79. package/scripts/setup.js +205 -10
  80. /package/app/components/renderers/{ConfigRenderer.tsx → config/ConfigRenderer.tsx} +0 -0
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useEffect, useCallback } from 'react';
4
- import { RefreshCw, AlertCircle, CheckCircle2, Loader2, GitBranch, Copy, Check, ExternalLink } from 'lucide-react';
4
+ import { RefreshCw, AlertCircle, CheckCircle2, Loader2, GitBranch, ExternalLink, Eye, EyeOff } from 'lucide-react';
5
5
  import { SectionLabel } from './Primitives';
6
6
  import { apiFetch } from '@/lib/api';
7
7
 
@@ -32,35 +32,53 @@ export function timeAgo(iso: string | null | undefined): string {
32
32
  return `${Math.floor(diff / 86400000)}d ago`;
33
33
  }
34
34
 
35
- /* ── Copy-to-clipboard button ──────────────────────────────────── */
35
+ /* ── Empty state GUI sync init form ─────────────────────────── */
36
36
 
37
- function CopyButton({ text }: { text: string }) {
38
- const [copied, setCopied] = useState(false);
39
- const handleCopy = async () => {
40
- await navigator.clipboard.writeText(text);
41
- setCopied(true);
42
- setTimeout(() => setCopied(false), 2000);
43
- };
44
- return (
45
- <button
46
- type="button"
47
- onClick={handleCopy}
48
- className="p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors shrink-0"
49
- title="Copy command"
50
- >
51
- {copied ? <Check size={12} className="text-green-500" /> : <Copy size={12} />}
52
- </button>
53
- );
37
+ function isValidGitUrl(url: string): 'https' | 'ssh' | false {
38
+ if (/^https:\/\/.+/.test(url)) return 'https';
39
+ if (/^git@[\w.-]+:.+/.test(url)) return 'ssh';
40
+ return false;
54
41
  }
55
42
 
56
- /* ── Empty state (Task D) ──────────────────────────────────────── */
57
-
58
- function SyncEmptyState({ t }: { t: any }) {
43
+ function SyncEmptyState({ t, onInitComplete }: { t: any; onInitComplete: () => void }) {
59
44
  const syncT = t.settings?.sync;
60
- const cmd = 'mindos sync init';
45
+
46
+ const [remoteUrl, setRemoteUrl] = useState('');
47
+ const [token, setToken] = useState('');
48
+ const [branch, setBranch] = useState('main');
49
+ const [showToken, setShowToken] = useState(false);
50
+ const [connecting, setConnecting] = useState(false);
51
+ const [error, setError] = useState('');
52
+
53
+ const urlType = remoteUrl.trim() ? isValidGitUrl(remoteUrl.trim()) : null;
54
+ const isValid = urlType === 'https' || urlType === 'ssh';
55
+ const showTokenField = urlType === 'https';
56
+
57
+ const handleConnect = async () => {
58
+ setConnecting(true);
59
+ setError('');
60
+ try {
61
+ await apiFetch('/api/sync', {
62
+ method: 'POST',
63
+ headers: { 'Content-Type': 'application/json' },
64
+ body: JSON.stringify({
65
+ action: 'init',
66
+ remote: remoteUrl.trim(),
67
+ token: token.trim() || undefined,
68
+ branch: branch.trim() || 'main',
69
+ }),
70
+ });
71
+ onInitComplete();
72
+ } catch (err: unknown) {
73
+ const msg = err instanceof Error ? err.message : 'Connection failed';
74
+ setError(msg);
75
+ } finally {
76
+ setConnecting(false);
77
+ }
78
+ };
61
79
 
62
80
  return (
63
- <div className="space-y-6">
81
+ <div className="space-y-5">
64
82
  {/* Header */}
65
83
  <div className="flex items-center gap-3">
66
84
  <div className="w-9 h-9 rounded-lg bg-muted flex items-center justify-center shrink-0">
@@ -76,33 +94,104 @@ function SyncEmptyState({ t }: { t: any }) {
76
94
  </div>
77
95
  </div>
78
96
 
79
- {/* Steps */}
80
- <div className="space-y-3">
81
- <SectionLabel>{syncT?.emptyStepsTitle ?? 'Setup'}</SectionLabel>
82
- <ol className="space-y-2.5 text-xs text-muted-foreground">
83
- <li className="flex items-start gap-2.5">
84
- <span className="w-5 h-5 rounded-full bg-muted flex items-center justify-center shrink-0 text-[10px] font-medium text-foreground mt-0.5">1</span>
85
- <span>{syncT?.emptyStep1 ?? 'Create a private Git repo (GitHub, GitLab, etc.) or use an existing one.'}</span>
86
- </li>
87
- <li className="flex items-start gap-2.5">
88
- <span className="w-5 h-5 rounded-full bg-muted flex items-center justify-center shrink-0 text-[10px] font-medium text-foreground mt-0.5">2</span>
89
- <div className="flex-1">
90
- <span>{syncT?.emptyStep2 ?? 'Run this command in your terminal:'}</span>
91
- <div className="flex items-center gap-1.5 mt-1.5 px-3 py-2 bg-muted rounded-lg font-mono text-xs text-foreground">
92
- <code className="flex-1 select-all">{cmd}</code>
93
- <CopyButton text={cmd} />
94
- </div>
95
- </div>
96
- </li>
97
- <li className="flex items-start gap-2.5">
98
- <span className="w-5 h-5 rounded-full bg-muted flex items-center justify-center shrink-0 text-[10px] font-medium text-foreground mt-0.5">3</span>
99
- <span>{syncT?.emptyStep3 ?? 'Follow the prompts to connect your repo. Sync starts automatically.'}</span>
100
- </li>
101
- </ol>
97
+ {/* Git Remote URL */}
98
+ <div className="space-y-1.5">
99
+ <label className="text-xs font-medium text-foreground block">
100
+ {syncT?.remoteUrl ?? 'Git Remote URL'}
101
+ </label>
102
+ <input
103
+ type="text"
104
+ value={remoteUrl}
105
+ onChange={e => { setRemoteUrl(e.target.value); setError(''); }}
106
+ placeholder="https://github.com/user/my-mind.git"
107
+ className="w-full px-3 py-2 text-sm rounded-lg border bg-transparent font-mono text-xs transition-colors focus:outline-none focus:ring-1"
108
+ style={{
109
+ borderColor: remoteUrl.trim() && !isValid ? 'var(--destructive, red)' : 'var(--border)',
110
+ color: 'var(--foreground)',
111
+ }}
112
+ />
113
+ {remoteUrl.trim() && !isValid && (
114
+ <p className="text-[11px]" style={{ color: 'var(--destructive, red)' }}>
115
+ {syncT?.invalidUrl ?? 'Invalid Git URL — use HTTPS (https://...) or SSH (git@...)'}
116
+ </p>
117
+ )}
118
+ {urlType === 'ssh' && (
119
+ <p className="text-[11px] text-muted-foreground flex items-center gap-1">
120
+ <AlertCircle size={11} className="shrink-0" />
121
+ {syncT?.sshHint ?? 'SSH URLs require SSH key configured on this machine. HTTPS with token recommended.'}
122
+ </p>
123
+ )}
102
124
  </div>
103
125
 
126
+ {/* Access Token (HTTPS only) */}
127
+ {showTokenField && (
128
+ <div className="space-y-1.5">
129
+ <label className="text-xs font-medium text-foreground block">
130
+ {syncT?.accessToken ?? 'Access Token'}{' '}
131
+ <span className="text-muted-foreground font-normal">{syncT?.optional ?? '(optional, for private repos)'}</span>
132
+ </label>
133
+ <div className="relative">
134
+ <input
135
+ type={showToken ? 'text' : 'password'}
136
+ value={token}
137
+ onChange={e => setToken(e.target.value)}
138
+ placeholder="ghp_xxxxxxxxxxxx"
139
+ className="w-full px-3 py-2 pr-9 text-sm rounded-lg border bg-transparent font-mono text-xs transition-colors focus:outline-none focus:ring-1"
140
+ style={{ borderColor: 'var(--border)', color: 'var(--foreground)' }}
141
+ />
142
+ <button
143
+ type="button"
144
+ onClick={() => setShowToken(!showToken)}
145
+ className="absolute right-2 top-1/2 -translate-y-1/2 p-0.5 rounded hover:bg-muted text-muted-foreground transition-colors"
146
+ >
147
+ {showToken ? <EyeOff size={14} /> : <Eye size={14} />}
148
+ </button>
149
+ </div>
150
+ <p className="text-[11px] text-muted-foreground">
151
+ {syncT?.tokenHint ?? 'GitHub: Settings → Developer settings → Personal access tokens → repo scope'}
152
+ </p>
153
+ </div>
154
+ )}
155
+
156
+ {/* Branch */}
157
+ <div className="space-y-1.5">
158
+ <label className="text-xs font-medium text-foreground block">
159
+ {syncT?.branchLabel ?? 'Branch'}
160
+ </label>
161
+ <input
162
+ type="text"
163
+ value={branch}
164
+ onChange={e => setBranch(e.target.value)}
165
+ placeholder="main"
166
+ className="w-full max-w-[200px] px-3 py-2 text-sm rounded-lg border bg-transparent font-mono text-xs transition-colors focus:outline-none focus:ring-1"
167
+ style={{ borderColor: 'var(--border)', color: 'var(--foreground)' }}
168
+ />
169
+ </div>
170
+
171
+ {/* Connect button */}
172
+ <button
173
+ type="button"
174
+ onClick={handleConnect}
175
+ disabled={!isValid || connecting}
176
+ className="flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
177
+ style={{ background: 'var(--amber)', color: '#131210' }}
178
+ >
179
+ {connecting && <Loader2 size={14} className="animate-spin" />}
180
+ {connecting
181
+ ? (syncT?.connecting ?? 'Connecting...')
182
+ : (syncT?.connectButton ?? 'Connect & Start Sync')}
183
+ </button>
184
+
185
+ {/* Error */}
186
+ {error && (
187
+ <div className="flex items-start gap-2 text-xs p-3 rounded-lg" role="alert" aria-live="polite" style={{ background: 'rgba(239,68,68,0.1)', color: 'var(--destructive, red)' }}>
188
+ <AlertCircle size={13} className="shrink-0 mt-0.5" />
189
+ <span>{error}</span>
190
+ </div>
191
+ )}
192
+
104
193
  {/* Features */}
105
- <div className="grid grid-cols-2 gap-2 text-[11px] text-muted-foreground">
194
+ <div className="grid grid-cols-2 gap-2 text-[11px] text-muted-foreground pt-2">
106
195
  {[
107
196
  syncT?.featureAutoCommit ?? 'Auto-commit on save',
108
197
  syncT?.featureAutoPull ?? 'Auto-pull from remote',
@@ -190,7 +279,7 @@ export function SyncTab({ t }: SyncTabProps) {
190
279
  }
191
280
 
192
281
  if (!status || !status.enabled) {
193
- return <SyncEmptyState t={t} />;
282
+ return <SyncEmptyState t={t} onInitComplete={fetchStatus} />;
194
283
  }
195
284
 
196
285
  const conflicts = status.conflicts || [];
@@ -256,7 +345,7 @@ export function SyncTab({ t }: SyncTabProps) {
256
345
 
257
346
  {/* Message */}
258
347
  {message && (
259
- <div className="flex items-center gap-1.5 text-xs">
348
+ <div className="flex items-center gap-1.5 text-xs" role="status" aria-live="polite">
260
349
  {message.type === 'success' ? (
261
350
  <><CheckCircle2 size={13} className="text-green-500" /><span className="text-green-500">{message.text}</span></>
262
351
  ) : (
@@ -24,7 +24,7 @@ export interface SettingsData {
24
24
  envValues?: Record<string, string>;
25
25
  }
26
26
 
27
- export type Tab = 'ai' | 'appearance' | 'knowledge' | 'plugins' | 'shortcuts' | 'sync';
27
+ export type Tab = 'ai' | 'appearance' | 'knowledge' | 'mcp' | 'plugins' | 'shortcuts' | 'sync';
28
28
 
29
29
  export const CONTENT_WIDTHS = [
30
30
  { value: '680px', label: 'Narrow (680px)' },
Binary file
package/app/lib/i18n.ts CHANGED
@@ -16,6 +16,7 @@ export const messages = {
16
16
  plugins: 'Plugins',
17
17
  showMore: 'Show more',
18
18
  showLess: 'Show less',
19
+ createToActivate: 'Create {file} to activate',
19
20
  shortcuts: {
20
21
  searchFiles: 'Search files',
21
22
  askAI: 'Ask AI',
@@ -96,10 +97,16 @@ export const messages = {
96
97
  listView: 'List view',
97
98
  emptyFolder: 'This folder is empty.',
98
99
  fileCount: (n: number) => `${n} files`,
100
+ newFile: 'New file',
101
+ },
102
+ findInPage: {
103
+ placeholder: 'Find in document…',
104
+ matchCount: (current: number, total: number) => `${current} of ${total}`,
105
+ noResults: 'No results',
99
106
  },
100
107
  settings: {
101
108
  title: 'Settings',
102
- tabs: { ai: 'AI', appearance: 'Appearance', knowledge: 'Knowledge Base', sync: 'Sync', plugins: 'Plugins', shortcuts: 'Shortcuts' },
109
+ tabs: { ai: 'AI', appearance: 'Appearance', knowledge: 'Knowledge Base', sync: 'Sync', mcp: 'MCP', plugins: 'Plugins', shortcuts: 'Shortcuts' },
103
110
  ai: {
104
111
  provider: 'Provider',
105
112
  model: 'Model',
@@ -144,14 +151,19 @@ export const messages = {
144
151
  sync: {
145
152
  emptyTitle: 'Cross-device Sync',
146
153
  emptyDesc: 'Automatically sync your knowledge base across devices via Git.',
147
- emptyStepsTitle: 'Setup',
148
- emptyStep1: 'Create a private Git repo (GitHub, GitLab, etc.) or use an existing one.',
149
- emptyStep2: 'Run this command in your terminal:',
150
- emptyStep3: 'Follow the prompts to connect your repo. Sync starts automatically.',
151
154
  featureAutoCommit: 'Auto-commit on save',
152
155
  featureAutoPull: 'Auto-pull from remote',
153
156
  featureConflict: 'Conflict detection',
154
157
  featureMultiDevice: 'Works across devices',
158
+ remoteUrl: 'Git Remote URL',
159
+ invalidUrl: 'Invalid Git URL — use HTTPS (https://...) or SSH (git@...)',
160
+ sshHint: 'SSH URLs require SSH key configured on this machine. HTTPS with token recommended.',
161
+ accessToken: 'Access Token',
162
+ optional: '(optional, for private repos)',
163
+ tokenHint: 'GitHub: Settings → Developer settings → Personal access tokens → repo scope',
164
+ branchLabel: 'Branch',
165
+ connectButton: 'Connect & Start Sync',
166
+ connecting: 'Connecting...',
155
167
  },
156
168
  plugins: {
157
169
  title: 'Installed Renderers',
@@ -162,6 +174,54 @@ export const messages = {
162
174
  noPlugins: 'No renderers installed.',
163
175
  comingSoon: 'Plugin marketplace coming soon.',
164
176
  },
177
+ mcp: {
178
+ serverTitle: 'MindOS MCP Server',
179
+ status: 'Status',
180
+ running: 'Running',
181
+ stopped: 'Stopped',
182
+ transport: 'Transport',
183
+ endpoint: 'Endpoint',
184
+ tools: 'Tools',
185
+ toolsRegistered: (n: number) => `${n} registered`,
186
+ auth: 'Auth',
187
+ authSet: 'Token set',
188
+ authNotSet: 'No token',
189
+ copyEndpoint: 'Copy Endpoint',
190
+ copyConfig: 'Copy Config',
191
+ copied: 'Copied!',
192
+ agentsTitle: 'Agent Configuration',
193
+ agent: 'Agent',
194
+ scope: 'Scope',
195
+ project: 'Project',
196
+ global: 'Global',
197
+ installed: 'Installed',
198
+ notInstalled: 'Not installed',
199
+ transportStdio: 'stdio (recommended)',
200
+ transportHttp: 'http',
201
+ httpUrl: 'MCP URL',
202
+ httpToken: 'Auth Token',
203
+ installSelected: 'Install Selected',
204
+ installing: 'Installing...',
205
+ installSuccess: (n: number) => `${n} agent(s) configured`,
206
+ installFailed: 'Install failed',
207
+ portLabel: 'MCP Port',
208
+ portHint: 'Changes require server restart',
209
+ skillsTitle: 'Skills',
210
+ skillAutoLoaded: 'Auto-loaded on every request',
211
+ skillSource: 'Source',
212
+ skillBuiltin: 'Built-in',
213
+ skillUser: 'Custom',
214
+ addSkill: '+ Add Skill',
215
+ deleteSkill: 'Delete',
216
+ editSkill: 'Edit',
217
+ saveSkill: 'Save',
218
+ cancelSkill: 'Cancel',
219
+ skillName: 'Name',
220
+ skillDesc: 'Description',
221
+ skillContent: 'Content',
222
+ skillNameConflict: 'A skill with this name already exists',
223
+ skillDeleteConfirm: (name: string) => `Delete skill "${name}"? This cannot be undone.`,
224
+ },
165
225
  save: 'Save',
166
226
  saved: 'Saved',
167
227
  saveFailed: 'Save failed',
@@ -186,8 +246,32 @@ export const messages = {
186
246
  { keys: ['Esc'], description: 'Cancel edit / close modal' },
187
247
  { keys: ['@'], description: 'Attach file in Ask AI' },
188
248
  ],
249
+ login: {
250
+ tagline: 'You think here, Agents act there.',
251
+ subtitle: 'Enter your password to continue',
252
+ passwordLabel: 'Password',
253
+ passwordPlaceholder: 'Enter password',
254
+ signIn: 'Sign in',
255
+ signingIn: 'Signing in…',
256
+ incorrectPassword: 'Incorrect password. Please try again.',
257
+ connectionError: 'Connection error. Please try again.',
258
+ },
259
+ notFound: {
260
+ title: 'File not found',
261
+ description: 'This file does not exist in your knowledge base.',
262
+ createButton: 'Create this file',
263
+ creating: 'Creating...',
264
+ goToParent: 'Go to parent folder',
265
+ goHome: 'Home',
266
+ },
267
+ updateBanner: {
268
+ newVersion: (latest: string, current: string) => `MindOS v${latest} available (current: v${current})`,
269
+ runUpdate: 'Run',
270
+ orSee: 'or',
271
+ releaseNotes: 'view release notes',
272
+ },
189
273
  setup: {
190
- stepTitles: ['Knowledge Base', 'AI Provider', 'Ports', 'Security', 'Review'],
274
+ stepTitles: ['Knowledge Base', 'AI Provider', 'Ports', 'Security', 'Agent Tools', 'Review'],
191
275
  // Step 1
192
276
  kbPath: 'Knowledge base path',
193
277
  kbPathHint: 'Absolute path to your notes directory.',
@@ -207,6 +291,11 @@ export const messages = {
207
291
  mcpPort: 'MCP server port',
208
292
  portHint: 'Valid range: 1024–65535',
209
293
  portRestartWarning: 'Port changes take effect after server restart.',
294
+ portInUse: (p: number) => `Port ${p} is already in use.`,
295
+ portSuggest: (p: number) => `Use ${p}`,
296
+ portChecking: 'Checking…',
297
+ portConflict: 'Web UI and MCP ports must be different.',
298
+ portVerifyHint: 'Click outside each field to verify, or wait for auto-check.',
210
299
  // Step 4
211
300
  authToken: 'Auth Token',
212
301
  authTokenHint: 'Bearer token for MCP / API clients. Auto-generated.',
@@ -217,16 +306,30 @@ export const messages = {
217
306
  copiedToken: 'Copied!',
218
307
  webPassword: 'Web UI Password',
219
308
  webPasswordHint: 'Optional. Protect browser access with a password.',
220
- // Step 5
221
- reviewTitle: 'Review Configuration',
309
+ // Step 5 — Agent Tools
310
+ agentToolsTitle: 'Agent Tools',
311
+ agentToolsHint: 'Select AI agents to configure with MindOS MCP. Agents marked "not installed" can be configured now — they will work once you install the app.',
312
+ agentTransport: 'Transport',
313
+ agentScope: 'Scope',
314
+ agentToolsLoading: 'Loading agents…',
315
+ agentToolsEmpty: 'No supported agents detected.',
316
+ agentNoneSelected: 'No agents selected — you can configure later in Settings → MCP.',
317
+ agentNotInstalled: 'not installed',
318
+ agentStatusOk: 'configured',
319
+ agentStatusError: 'failed',
320
+ agentInstalling: 'Configuring…',
321
+ // Step 2 — AI skip card
322
+ aiSkipTitle: 'Skip for now',
323
+ aiSkipDesc: 'You can add an API key later in Settings → AI.',
324
+ // Step 6 — Review
222
325
  reviewHint: 'Verify your settings before completing setup.',
223
- keyMasked: (key: string) => key.slice(0, 6) + '•••' + key.slice(-3),
326
+ reviewInstallResults: 'Agent configuration results:',
327
+ portAvailable: 'Available',
224
328
  portChanged: 'Port changed — please restart the server for it to take effect.',
225
329
  // Buttons
226
330
  back: 'Back',
227
331
  next: 'Next',
228
332
  complete: 'Complete Setup',
229
- skip: 'Skip',
230
333
  // Status
231
334
  completing: 'Saving...',
232
335
  completeDone: 'Setup complete!',
@@ -248,6 +351,7 @@ export const messages = {
248
351
  plugins: '插件',
249
352
  showMore: '查看更多',
250
353
  showLess: '收起',
354
+ createToActivate: '创建 {file} 以启用此插件',
251
355
  shortcuts: {
252
356
  searchFiles: '搜索文件',
253
357
  askAI: '问 AI',
@@ -328,10 +432,16 @@ export const messages = {
328
432
  listView: '列表视图',
329
433
  emptyFolder: '此目录为空。',
330
434
  fileCount: (n: number) => `${n} 个文件`,
435
+ newFile: '新建文件',
436
+ },
437
+ findInPage: {
438
+ placeholder: '在文档中查找…',
439
+ matchCount: (current: number, total: number) => `${current} / ${total}`,
440
+ noResults: '无结果',
331
441
  },
332
442
  settings: {
333
443
  title: '设置',
334
- tabs: { ai: 'AI', appearance: '外观', knowledge: '知识库', sync: '同步', plugins: '插件', shortcuts: '快捷键' },
444
+ tabs: { ai: 'AI', appearance: '外观', knowledge: '知识库', sync: '同步', mcp: 'MCP', plugins: '插件', shortcuts: '快捷键' },
335
445
  ai: {
336
446
  provider: '服务商',
337
447
  model: '模型',
@@ -376,14 +486,19 @@ export const messages = {
376
486
  sync: {
377
487
  emptyTitle: '跨设备同步',
378
488
  emptyDesc: '通过 Git 自动同步知识库到所有设备。',
379
- emptyStepsTitle: '配置步骤',
380
- emptyStep1: '创建一个私有 Git 仓库(GitHub、GitLab 等),或使用现有仓库。',
381
- emptyStep2: '在终端中运行以下命令:',
382
- emptyStep3: '按照提示连接仓库,同步将自动开始。',
383
489
  featureAutoCommit: '保存时自动提交',
384
490
  featureAutoPull: '自动拉取远程更新',
385
491
  featureConflict: '冲突检测',
386
492
  featureMultiDevice: '多设备同步',
493
+ remoteUrl: 'Git 远程仓库 URL',
494
+ invalidUrl: '无效的 Git URL — 请使用 HTTPS (https://...) 或 SSH (git@...) 格式',
495
+ sshHint: 'SSH URL 需要在本机配置 SSH 密钥。推荐使用 HTTPS + Token。',
496
+ accessToken: '访问令牌',
497
+ optional: '(可选,私有仓库需要)',
498
+ tokenHint: 'GitHub: Settings → Developer settings → Personal access tokens → repo scope',
499
+ branchLabel: '分支',
500
+ connectButton: '连接并开始同步',
501
+ connecting: '连接中...',
387
502
  },
388
503
  plugins: {
389
504
  title: '已安装渲染器',
@@ -394,6 +509,54 @@ export const messages = {
394
509
  noPlugins: '暂无渲染器。',
395
510
  comingSoon: '插件市场即将上线。',
396
511
  },
512
+ mcp: {
513
+ serverTitle: 'MindOS MCP 服务',
514
+ status: '状态',
515
+ running: '运行中',
516
+ stopped: '已停止',
517
+ transport: '传输方式',
518
+ endpoint: '端点',
519
+ tools: '工具',
520
+ toolsRegistered: (n: number) => `已注册 ${n} 个`,
521
+ auth: '认证',
522
+ authSet: '已设置 Token',
523
+ authNotSet: '未设置',
524
+ copyEndpoint: '复制端点',
525
+ copyConfig: '复制配置',
526
+ copied: '已复制!',
527
+ agentsTitle: 'Agent 配置',
528
+ agent: 'Agent',
529
+ scope: '范围',
530
+ project: '项目',
531
+ global: '全局',
532
+ installed: '已安装',
533
+ notInstalled: '未安装',
534
+ transportStdio: 'stdio(推荐)',
535
+ transportHttp: 'http',
536
+ httpUrl: 'MCP URL',
537
+ httpToken: '认证 Token',
538
+ installSelected: '安装选中',
539
+ installing: '安装中...',
540
+ installSuccess: (n: number) => `已配置 ${n} 个 agent`,
541
+ installFailed: '安装失败',
542
+ portLabel: 'MCP 端口',
543
+ portHint: '修改后需重启服务',
544
+ skillsTitle: 'Skills',
545
+ skillAutoLoaded: '每次请求自动加载',
546
+ skillSource: '来源',
547
+ skillBuiltin: '内置',
548
+ skillUser: '自定义',
549
+ addSkill: '+ 添加 Skill',
550
+ deleteSkill: '删除',
551
+ editSkill: '编辑',
552
+ saveSkill: '保存',
553
+ cancelSkill: '取消',
554
+ skillName: '名称',
555
+ skillDesc: '描述',
556
+ skillContent: '内容',
557
+ skillNameConflict: '同名 skill 已存在',
558
+ skillDeleteConfirm: (name: string) => `确定删除「${name}」?此操作不可撤销。`,
559
+ },
397
560
  save: '保存',
398
561
  saved: '已保存',
399
562
  saveFailed: '保存失败',
@@ -418,8 +581,32 @@ export const messages = {
418
581
  { keys: ['Esc'], description: '取消编辑 / 关闭弹窗' },
419
582
  { keys: ['@'], description: '在 AI 对话中添加附件' },
420
583
  ],
584
+ login: {
585
+ tagline: '人类在此思考,Agent 依此行动。',
586
+ subtitle: '请输入密码以继续',
587
+ passwordLabel: '密码',
588
+ passwordPlaceholder: '输入密码',
589
+ signIn: '登录',
590
+ signingIn: '登录中…',
591
+ incorrectPassword: '密码错误,请重试。',
592
+ connectionError: '连接错误,请重试。',
593
+ },
594
+ notFound: {
595
+ title: '文件未找到',
596
+ description: '该文件不存在于你的知识库中。',
597
+ createButton: '创建此文件',
598
+ creating: '创建中...',
599
+ goToParent: '返回上级目录',
600
+ goHome: '首页',
601
+ },
602
+ updateBanner: {
603
+ newVersion: (latest: string, current: string) => `MindOS v${latest} 可用(当前 v${current})`,
604
+ runUpdate: '终端运行',
605
+ orSee: '或',
606
+ releaseNotes: '查看更新说明',
607
+ },
421
608
  setup: {
422
- stepTitles: ['知识库', 'AI 服务商', '端口', '安全', '确认'],
609
+ stepTitles: ['知识库', 'AI 服务商', '端口', '安全', 'Agent 工具', '确认'],
423
610
  // Step 1
424
611
  kbPath: '知识库路径',
425
612
  kbPathHint: '笔记目录的绝对路径。',
@@ -439,6 +626,11 @@ export const messages = {
439
626
  mcpPort: 'MCP 服务端口',
440
627
  portHint: '有效范围:1024–65535',
441
628
  portRestartWarning: '端口修改需重启服务后生效。',
629
+ portInUse: (p: number) => `端口 ${p} 已被占用。`,
630
+ portSuggest: (p: number) => `使用 ${p}`,
631
+ portChecking: '检测中…',
632
+ portConflict: 'Web UI 端口和 MCP 端口不能相同。',
633
+ portVerifyHint: '点击输入框外部验证,或等待自动检测。',
442
634
  // Step 4
443
635
  authToken: 'Auth Token',
444
636
  authTokenHint: 'MCP / API 客户端使用的 Bearer Token,自动生成。',
@@ -449,10 +641,25 @@ export const messages = {
449
641
  copiedToken: '已复制!',
450
642
  webPassword: '网页访问密码',
451
643
  webPasswordHint: '可选。设置后浏览器访问需要登录。',
452
- // Step 5
453
- reviewTitle: '确认配置',
644
+ // Step 5 — Agent Tools
645
+ agentToolsTitle: 'Agent 工具',
646
+ agentToolsHint: '选择要与 MindOS MCP 配置的 AI Agent。标注「未安装」的 agent 可以先行配置,安装应用后即可生效。',
647
+ agentTransport: '传输方式',
648
+ agentScope: '范围',
649
+ agentToolsLoading: '正在加载 Agent…',
650
+ agentToolsEmpty: '未检测到受支持的 Agent。',
651
+ agentNoneSelected: '未选择 agent — 可稍后在 设置 → MCP 中配置。',
652
+ agentNotInstalled: '未安装',
653
+ agentStatusOk: '已配置',
654
+ agentStatusError: '失败',
655
+ agentInstalling: '配置中…',
656
+ // Step 2 — AI skip card
657
+ aiSkipTitle: '暂时跳过',
658
+ aiSkipDesc: '稍后可在 设置 → AI 中添加 API 密钥。',
659
+ // Step 6 — Review
454
660
  reviewHint: '完成设置前请确认以下信息。',
455
- keyMasked: (key: string) => key.slice(0, 6) + '•••' + key.slice(-3),
661
+ reviewInstallResults: 'Agent 配置结果:',
662
+ portAvailable: '可用',
456
663
  portChanged: '端口已变更 — 请重启服务以使其生效。',
457
664
  // Buttons
458
665
  back: '上一步',