@geminilight/mindos 0.6.25 → 0.6.28
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/README.md +19 -3
- package/README_zh.md +19 -3
- package/app/app/api/a2a/discover/route.ts +23 -0
- package/app/components/CreateSpaceModal.tsx +1 -0
- package/app/components/ImportModal.tsx +3 -0
- package/app/components/OnboardingView.tsx +1 -0
- package/app/components/RightAskPanel.tsx +4 -2
- package/app/components/SidebarLayout.tsx +11 -2
- package/app/components/agents/AgentDetailContent.tsx +48 -1
- package/app/components/agents/AgentsContentPage.tsx +3 -0
- package/app/components/agents/AgentsOverviewSection.tsx +11 -0
- package/app/components/agents/DiscoverAgentModal.tsx +149 -0
- package/app/components/ask/AskContent.tsx +29 -9
- package/app/components/ask/SessionTabBar.tsx +70 -0
- package/app/components/echo/EchoInsightCollapsible.tsx +4 -0
- package/app/components/help/HelpContent.tsx +65 -9
- package/app/components/panels/AgentsPanel.tsx +25 -2
- package/app/components/panels/AgentsPanelAgentListRow.tsx +10 -1
- package/app/components/renderers/workflow/WorkflowRenderer.tsx +5 -0
- package/app/components/settings/AiTab.tsx +1 -0
- package/app/components/settings/KnowledgeTab.tsx +2 -0
- package/app/components/settings/SyncTab.tsx +2 -0
- package/app/components/setup/StepDots.tsx +5 -1
- package/app/data/skills/mindos/SKILL.md +186 -0
- package/app/data/skills/mindos-zh/SKILL.md +185 -0
- package/app/hooks/useA2aRegistry.ts +53 -0
- package/app/hooks/useAskSession.ts +44 -25
- package/app/lib/a2a/a2a-tools.ts +212 -0
- package/app/lib/a2a/client.ts +207 -0
- package/app/lib/a2a/index.ts +8 -0
- package/app/lib/a2a/orchestrator.ts +255 -0
- package/app/lib/a2a/types.ts +54 -0
- package/app/lib/agent/tools.ts +6 -4
- package/app/lib/i18n-en.ts +52 -0
- package/app/lib/i18n-zh.ts +52 -0
- package/app/next-env.d.ts +1 -1
- package/bin/cli.js +180 -171
- package/bin/commands/agent.js +110 -18
- package/bin/commands/api.js +5 -3
- package/bin/commands/ask.js +3 -3
- package/bin/commands/file.js +13 -13
- package/bin/commands/search.js +2 -2
- package/bin/commands/space.js +64 -10
- package/bin/lib/command.js +10 -0
- package/package.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Plus, X } from 'lucide-react';
|
|
4
|
+
import type { ChatSession } from '@/lib/types';
|
|
5
|
+
import { sessionTitle } from '@/hooks/useAskSession';
|
|
6
|
+
import { useLocale } from '@/lib/LocaleContext';
|
|
7
|
+
|
|
8
|
+
interface SessionTabBarProps {
|
|
9
|
+
sessions: ChatSession[];
|
|
10
|
+
activeSessionId: string | null;
|
|
11
|
+
onLoad: (id: string) => void;
|
|
12
|
+
onDelete: (id: string) => void;
|
|
13
|
+
onNew: () => void;
|
|
14
|
+
maxTabs?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function SessionTabBar({
|
|
18
|
+
sessions, activeSessionId, onLoad, onDelete, onNew, maxTabs = 3,
|
|
19
|
+
}: SessionTabBarProps) {
|
|
20
|
+
const { t } = useLocale();
|
|
21
|
+
const visibleSessions = sessions.slice(0, maxTabs);
|
|
22
|
+
|
|
23
|
+
if (visibleSessions.length === 0) return null;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="flex items-center border-b border-border shrink-0 bg-background/50">
|
|
27
|
+
<div className="flex flex-1 min-w-0">
|
|
28
|
+
{visibleSessions.map((s) => {
|
|
29
|
+
const isActive = s.id === activeSessionId;
|
|
30
|
+
const title = sessionTitle(s);
|
|
31
|
+
return (
|
|
32
|
+
<button
|
|
33
|
+
key={s.id}
|
|
34
|
+
type="button"
|
|
35
|
+
onClick={() => onLoad(s.id)}
|
|
36
|
+
className={`group relative flex items-center gap-1 min-w-0 max-w-[160px] px-3 py-2 text-xs transition-colors
|
|
37
|
+
${isActive
|
|
38
|
+
? 'text-foreground border-b-2 border-[var(--amber)] bg-card'
|
|
39
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-muted/50 border-b-2 border-transparent'
|
|
40
|
+
}`}
|
|
41
|
+
title={title}
|
|
42
|
+
>
|
|
43
|
+
<span className="truncate">{title === '(empty session)' ? t.hints.newChat : title}</span>
|
|
44
|
+
{visibleSessions.length > 1 && (
|
|
45
|
+
<span
|
|
46
|
+
role="button"
|
|
47
|
+
tabIndex={0}
|
|
48
|
+
onClick={(e) => { e.stopPropagation(); onDelete(s.id); }}
|
|
49
|
+
onKeyDown={(e) => { if (e.key === 'Enter') { e.stopPropagation(); onDelete(s.id); } }}
|
|
50
|
+
className="shrink-0 p-0.5 rounded opacity-0 group-hover:opacity-100 hover:bg-muted hover:text-error transition-opacity"
|
|
51
|
+
title={t.hints.closeSession}
|
|
52
|
+
>
|
|
53
|
+
<X size={10} />
|
|
54
|
+
</span>
|
|
55
|
+
)}
|
|
56
|
+
</button>
|
|
57
|
+
);
|
|
58
|
+
})}
|
|
59
|
+
</div>
|
|
60
|
+
<button
|
|
61
|
+
type="button"
|
|
62
|
+
onClick={onNew}
|
|
63
|
+
className="shrink-0 p-2 text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors"
|
|
64
|
+
title={t.hints.newChat}
|
|
65
|
+
>
|
|
66
|
+
<Plus size={13} />
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -7,6 +7,7 @@ import remarkGfm from 'remark-gfm';
|
|
|
7
7
|
import { cn } from '@/lib/utils';
|
|
8
8
|
import { consumeUIMessageStream } from '@/lib/agent/stream-consumer';
|
|
9
9
|
import { useSettingsAiAvailable } from '@/hooks/useSettingsAiAvailable';
|
|
10
|
+
import { useLocale } from '@/lib/LocaleContext';
|
|
10
11
|
|
|
11
12
|
const proseInsight =
|
|
12
13
|
'prose prose-sm prose-panel dark:prose-invert max-w-none text-foreground ' +
|
|
@@ -50,6 +51,7 @@ export function EchoInsightCollapsible({
|
|
|
50
51
|
const btnId = `${panelId}-btn`;
|
|
51
52
|
const abortRef = useRef<AbortController | null>(null);
|
|
52
53
|
const { ready: aiReady, loading: aiLoading } = useSettingsAiAvailable();
|
|
54
|
+
const { t } = useLocale();
|
|
53
55
|
|
|
54
56
|
useEffect(() => () => abortRef.current?.abort(), []);
|
|
55
57
|
|
|
@@ -145,6 +147,7 @@ export function EchoInsightCollapsible({
|
|
|
145
147
|
<button
|
|
146
148
|
type="button"
|
|
147
149
|
disabled={generateDisabled}
|
|
150
|
+
title={generateDisabled ? t.hints.aiNotConfigured : undefined}
|
|
148
151
|
onClick={runGenerate}
|
|
149
152
|
className="inline-flex items-center gap-2 rounded-lg bg-[var(--amber)] px-3 py-2 font-sans text-sm font-medium text-[var(--amber-foreground)] transition-opacity duration-150 hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
150
153
|
>
|
|
@@ -160,6 +163,7 @@ export function EchoInsightCollapsible({
|
|
|
160
163
|
type="button"
|
|
161
164
|
onClick={runGenerate}
|
|
162
165
|
disabled={streaming || !aiReady}
|
|
166
|
+
title={streaming || !aiReady ? t.hints.generationInProgress : undefined}
|
|
163
167
|
className="font-sans text-sm text-[var(--amber)] underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
|
|
164
168
|
>
|
|
165
169
|
{retryLabel}
|
|
@@ -5,7 +5,8 @@ import { BookOpen, Rocket, Brain, Keyboard, HelpCircle, Bot, ChevronDown, Copy,
|
|
|
5
5
|
import { useLocale } from '@/lib/LocaleContext';
|
|
6
6
|
|
|
7
7
|
/* ── Collapsible Section ── */
|
|
8
|
-
function Section({ icon, title, defaultOpen = false, children }: {
|
|
8
|
+
function Section({ id, icon, title, defaultOpen = false, children }: {
|
|
9
|
+
id?: string;
|
|
9
10
|
icon: React.ReactNode;
|
|
10
11
|
title: string;
|
|
11
12
|
defaultOpen?: boolean;
|
|
@@ -14,7 +15,7 @@ function Section({ icon, title, defaultOpen = false, children }: {
|
|
|
14
15
|
const [open, setOpen] = useState(defaultOpen);
|
|
15
16
|
|
|
16
17
|
return (
|
|
17
|
-
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
|
18
|
+
<div id={id} className="bg-card border border-border rounded-lg overflow-hidden scroll-mt-4">
|
|
18
19
|
<button
|
|
19
20
|
onClick={() => setOpen(v => !v)}
|
|
20
21
|
className="w-full flex items-center gap-3 px-5 py-4 text-left hover:bg-muted/50 transition-colors focus-visible:ring-2 focus-visible:ring-ring"
|
|
@@ -144,8 +145,63 @@ export default function HelpContent() {
|
|
|
144
145
|
{ keys: '@', label: h.shortcuts.attachFile },
|
|
145
146
|
], [mod, h.shortcuts]);
|
|
146
147
|
|
|
148
|
+
const tocItems = useMemo(() => [
|
|
149
|
+
{ id: 'what-is', label: h.whatIs.title },
|
|
150
|
+
{ id: 'concepts', label: h.concepts.title },
|
|
151
|
+
{ id: 'quick-start', label: h.quickStart.title },
|
|
152
|
+
{ id: 'agent-usage', label: h.agentUsage.title },
|
|
153
|
+
{ id: 'shortcuts', label: h.shortcutsTitle },
|
|
154
|
+
{ id: 'faq', label: h.faq.title },
|
|
155
|
+
], [h]);
|
|
156
|
+
|
|
157
|
+
const [activeSection, setActiveSection] = useState('');
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
const ids = tocItems.map(i => i.id);
|
|
160
|
+
const observer = new IntersectionObserver(
|
|
161
|
+
(entries) => {
|
|
162
|
+
for (const entry of entries) {
|
|
163
|
+
if (entry.isIntersecting) {
|
|
164
|
+
setActiveSection(entry.target.id);
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
{ rootMargin: '-10% 0px -80% 0px', threshold: 0 },
|
|
170
|
+
);
|
|
171
|
+
const timer = setTimeout(() => {
|
|
172
|
+
for (const id of ids) {
|
|
173
|
+
const el = document.getElementById(id);
|
|
174
|
+
if (el) observer.observe(el);
|
|
175
|
+
}
|
|
176
|
+
}, 100);
|
|
177
|
+
return () => { clearTimeout(timer); observer.disconnect(); };
|
|
178
|
+
}, [tocItems]);
|
|
179
|
+
|
|
147
180
|
return (
|
|
148
|
-
<div className="content-width px-4 md:px-6 py-8 md:py-12">
|
|
181
|
+
<div className="content-width px-4 md:px-6 py-8 md:py-12 relative">
|
|
182
|
+
{/* ── Floating TOC (wide screens only) ── */}
|
|
183
|
+
<nav className="hidden xl:block fixed top-24 w-44" style={{ left: 'calc(50% + 340px)' }} aria-label="Table of contents">
|
|
184
|
+
<ul className="space-y-1 border-l border-border pl-3">
|
|
185
|
+
{tocItems.map(item => (
|
|
186
|
+
<li key={item.id}>
|
|
187
|
+
<a
|
|
188
|
+
href={`#${item.id}`}
|
|
189
|
+
onClick={(e) => {
|
|
190
|
+
e.preventDefault();
|
|
191
|
+
document.getElementById(item.id)?.scrollIntoView({ behavior: 'smooth' });
|
|
192
|
+
}}
|
|
193
|
+
className={`block text-xs py-1 transition-colors ${
|
|
194
|
+
activeSection === item.id
|
|
195
|
+
? 'text-[var(--amber)] font-medium border-l-2 border-[var(--amber)] -ml-[13px] pl-[11px]'
|
|
196
|
+
: 'text-muted-foreground hover:text-foreground'
|
|
197
|
+
}`}
|
|
198
|
+
>
|
|
199
|
+
{item.label}
|
|
200
|
+
</a>
|
|
201
|
+
</li>
|
|
202
|
+
))}
|
|
203
|
+
</ul>
|
|
204
|
+
</nav>
|
|
149
205
|
{/* ── Header ── */}
|
|
150
206
|
<div className="mb-8">
|
|
151
207
|
<div className="flex items-center gap-2 mb-1">
|
|
@@ -158,12 +214,12 @@ export default function HelpContent() {
|
|
|
158
214
|
{/* ── Sections ── */}
|
|
159
215
|
<div className="space-y-3">
|
|
160
216
|
{/* 1. What is MindOS */}
|
|
161
|
-
<Section icon={<BookOpen size={18} />} title={h.whatIs.title} defaultOpen>
|
|
217
|
+
<Section id="what-is" icon={<BookOpen size={18} />} title={h.whatIs.title} defaultOpen>
|
|
162
218
|
<p className="text-sm text-muted-foreground leading-relaxed">{h.whatIs.body}</p>
|
|
163
219
|
</Section>
|
|
164
220
|
|
|
165
221
|
{/* 2. Core Concepts */}
|
|
166
|
-
<Section icon={<Brain size={18} />} title={h.concepts.title} defaultOpen>
|
|
222
|
+
<Section id="concepts" icon={<Brain size={18} />} title={h.concepts.title} defaultOpen>
|
|
167
223
|
<div className="space-y-4">
|
|
168
224
|
<div>
|
|
169
225
|
<p className="text-sm font-medium text-foreground">{h.concepts.spaceTitle}</p>
|
|
@@ -181,7 +237,7 @@ export default function HelpContent() {
|
|
|
181
237
|
</Section>
|
|
182
238
|
|
|
183
239
|
{/* 3. Quick Start */}
|
|
184
|
-
<Section icon={<Rocket size={18} />} title={h.quickStart.title} defaultOpen>
|
|
240
|
+
<Section id="quick-start" icon={<Rocket size={18} />} title={h.quickStart.title} defaultOpen>
|
|
185
241
|
<div className="space-y-4">
|
|
186
242
|
<StepCard step={1} title={h.quickStart.step1Title} desc={h.quickStart.step1Desc} />
|
|
187
243
|
<StepCard step={2} title={h.quickStart.step2Title} desc={h.quickStart.step2Desc} />
|
|
@@ -190,7 +246,7 @@ export default function HelpContent() {
|
|
|
190
246
|
</Section>
|
|
191
247
|
|
|
192
248
|
{/* 4. Using MindOS with AI Agents */}
|
|
193
|
-
<Section icon={<Bot size={18} />} title={h.agentUsage.title} defaultOpen>
|
|
249
|
+
<Section id="agent-usage" icon={<Bot size={18} />} title={h.agentUsage.title} defaultOpen>
|
|
194
250
|
<p className="text-sm text-muted-foreground leading-relaxed mb-4">{h.agentUsage.intro}</p>
|
|
195
251
|
|
|
196
252
|
<div className="space-y-3">
|
|
@@ -215,7 +271,7 @@ export default function HelpContent() {
|
|
|
215
271
|
</Section>
|
|
216
272
|
|
|
217
273
|
{/* 5. Keyboard Shortcuts */}
|
|
218
|
-
<Section icon={<Keyboard size={18} />} title={h.shortcutsTitle}>
|
|
274
|
+
<Section id="shortcuts" icon={<Keyboard size={18} />} title={h.shortcutsTitle}>
|
|
219
275
|
<div className="space-y-0">
|
|
220
276
|
{shortcuts.map((s) => (
|
|
221
277
|
<ShortcutRow key={s.keys} keys={s.keys} label={s.label} />
|
|
@@ -224,7 +280,7 @@ export default function HelpContent() {
|
|
|
224
280
|
</Section>
|
|
225
281
|
|
|
226
282
|
{/* 6. FAQ */}
|
|
227
|
-
<Section icon={<HelpCircle size={18} />} title={h.faq.title}>
|
|
283
|
+
<Section id="faq" icon={<HelpCircle size={18} />} title={h.faq.title}>
|
|
228
284
|
<div>
|
|
229
285
|
{h.faq.items.map((item, i) => (
|
|
230
286
|
<FaqItem key={i} q={item.q} a={item.a} />
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import { usePathname } from 'next/navigation';
|
|
5
|
-
import { Loader2, RefreshCw, Settings } from 'lucide-react';
|
|
5
|
+
import { Globe, Loader2, RefreshCw, Settings } from 'lucide-react';
|
|
6
6
|
import { useMcpData } from '@/hooks/useMcpData';
|
|
7
|
+
import { useA2aRegistry } from '@/hooks/useA2aRegistry';
|
|
7
8
|
import { useLocale } from '@/lib/LocaleContext';
|
|
8
9
|
import PanelHeader from './PanelHeader';
|
|
9
10
|
import { AgentsPanelHubNav } from './AgentsPanelHubNav';
|
|
10
11
|
import { AgentsPanelAgentGroups } from './AgentsPanelAgentGroups';
|
|
12
|
+
import DiscoverAgentModal from '../agents/DiscoverAgentModal';
|
|
11
13
|
|
|
12
14
|
interface AgentsPanelProps {
|
|
13
15
|
active: boolean;
|
|
@@ -28,6 +30,8 @@ export default function AgentsPanel({
|
|
|
28
30
|
const pathname = usePathname();
|
|
29
31
|
const [refreshing, setRefreshing] = useState(false);
|
|
30
32
|
const [showNotDetected, setShowNotDetected] = useState(false);
|
|
33
|
+
const [showDiscoverModal, setShowDiscoverModal] = useState(false);
|
|
34
|
+
const a2a = useA2aRegistry();
|
|
31
35
|
|
|
32
36
|
const handleRefresh = async () => {
|
|
33
37
|
setRefreshing(true);
|
|
@@ -82,6 +86,9 @@ export default function AgentsPanel({
|
|
|
82
86
|
{!mcp.loading && (
|
|
83
87
|
<span className="text-2xs text-muted-foreground">
|
|
84
88
|
{connected.length} {p.connected}
|
|
89
|
+
{a2a.agents.length > 0 && (
|
|
90
|
+
<> · {p.a2aLabel} {a2a.agents.length}</>
|
|
91
|
+
)}
|
|
85
92
|
</span>
|
|
86
93
|
)}
|
|
87
94
|
<button
|
|
@@ -146,7 +153,15 @@ export default function AgentsPanel({
|
|
|
146
153
|
)}
|
|
147
154
|
</div>
|
|
148
155
|
|
|
149
|
-
<div className="px-3 py-2 border-t border-border shrink-0">
|
|
156
|
+
<div className="px-3 py-2 border-t border-border shrink-0 space-y-1">
|
|
157
|
+
<button
|
|
158
|
+
type="button"
|
|
159
|
+
onClick={() => setShowDiscoverModal(true)}
|
|
160
|
+
className="flex items-center gap-1.5 text-2xs text-muted-foreground hover:text-foreground transition-colors w-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
|
|
161
|
+
>
|
|
162
|
+
<Globe size={11} />
|
|
163
|
+
{p.a2aDiscover}
|
|
164
|
+
</button>
|
|
150
165
|
<button
|
|
151
166
|
type="button"
|
|
152
167
|
onClick={openAdvancedConfig}
|
|
@@ -156,6 +171,14 @@ export default function AgentsPanel({
|
|
|
156
171
|
{p.advancedConfig}
|
|
157
172
|
</button>
|
|
158
173
|
</div>
|
|
174
|
+
|
|
175
|
+
<DiscoverAgentModal
|
|
176
|
+
open={showDiscoverModal}
|
|
177
|
+
onClose={() => setShowDiscoverModal(false)}
|
|
178
|
+
onDiscover={a2a.discover}
|
|
179
|
+
discovering={a2a.discovering}
|
|
180
|
+
error={a2a.error}
|
|
181
|
+
/>
|
|
159
182
|
</div>
|
|
160
183
|
);
|
|
161
184
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import Link from 'next/link';
|
|
5
|
-
import { Check, ChevronRight, Loader2, RotateCw } from 'lucide-react';
|
|
5
|
+
import { Check, ChevronRight, Globe, Loader2, RotateCw } from 'lucide-react';
|
|
6
6
|
import type { AgentInfo } from '../settings/types';
|
|
7
7
|
import { AgentAvatar } from '../agents/AgentsPrimitives';
|
|
8
8
|
|
|
@@ -23,6 +23,8 @@ export default function AgentsPanelAgentListRow({
|
|
|
23
23
|
detailHref,
|
|
24
24
|
onInstallAgent,
|
|
25
25
|
copy,
|
|
26
|
+
isA2aReady = false,
|
|
27
|
+
a2aTooltip,
|
|
26
28
|
}: {
|
|
27
29
|
agent: AgentInfo;
|
|
28
30
|
agentStatus: AgentsPanelAgentListStatus;
|
|
@@ -30,6 +32,8 @@ export default function AgentsPanelAgentListRow({
|
|
|
30
32
|
detailHref: string;
|
|
31
33
|
onInstallAgent: (key: string) => Promise<boolean>;
|
|
32
34
|
copy: AgentsPanelAgentListRowCopy;
|
|
35
|
+
isA2aReady?: boolean;
|
|
36
|
+
a2aTooltip?: string;
|
|
33
37
|
}) {
|
|
34
38
|
return (
|
|
35
39
|
<div
|
|
@@ -51,6 +55,11 @@ export default function AgentsPanelAgentListRow({
|
|
|
51
55
|
{agent.transport}
|
|
52
56
|
</span>
|
|
53
57
|
)}
|
|
58
|
+
{isA2aReady && (
|
|
59
|
+
<span title={a2aTooltip} className="shrink-0 text-muted-foreground/70">
|
|
60
|
+
<Globe size={12} />
|
|
61
|
+
</span>
|
|
62
|
+
)}
|
|
54
63
|
<span className="flex-1 min-w-[4px]" />
|
|
55
64
|
<ChevronRight
|
|
56
65
|
size={15}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useMemo, useState, useRef, useCallback } from 'react';
|
|
4
4
|
import { Play, SkipForward, RotateCcw, CheckCircle2, Circle, Loader2, AlertCircle, ChevronDown, Sparkles } from 'lucide-react';
|
|
5
5
|
import type { RendererContext } from '@/lib/renderers/registry';
|
|
6
|
+
import { useLocale } from '@/lib/LocaleContext';
|
|
6
7
|
|
|
7
8
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
8
9
|
|
|
@@ -171,6 +172,7 @@ function StepCard({
|
|
|
171
172
|
onSkip: () => void;
|
|
172
173
|
canRun: boolean;
|
|
173
174
|
}) {
|
|
175
|
+
const { t } = useLocale();
|
|
174
176
|
const [expanded, setExpanded] = useState(false);
|
|
175
177
|
const hasBody = step.body.trim().length > 0;
|
|
176
178
|
const hasOutput = step.output.length > 0;
|
|
@@ -201,6 +203,7 @@ function StepCard({
|
|
|
201
203
|
<button
|
|
202
204
|
onClick={onRun}
|
|
203
205
|
disabled={!canRun}
|
|
206
|
+
title={!canRun ? t.hints.workflowStepRunning : undefined}
|
|
204
207
|
style={{
|
|
205
208
|
display: 'flex', alignItems: 'center', gap: 4,
|
|
206
209
|
padding: '3px 10px', borderRadius: 6, fontSize: '0.72rem',
|
|
@@ -268,6 +271,7 @@ function StepCard({
|
|
|
268
271
|
// ─── Main renderer ────────────────────────────────────────────────────────────
|
|
269
272
|
|
|
270
273
|
export function WorkflowRenderer({ filePath, content }: RendererContext) {
|
|
274
|
+
const { t } = useLocale();
|
|
271
275
|
const parsed = useMemo(() => parseWorkflow(content), [content]);
|
|
272
276
|
const [steps, setSteps] = useState<WorkflowStep[]>(() => parsed.steps);
|
|
273
277
|
const [running, setRunning] = useState(false);
|
|
@@ -357,6 +361,7 @@ export function WorkflowRenderer({ filePath, content }: RendererContext) {
|
|
|
357
361
|
<button
|
|
358
362
|
onClick={() => runStep(nextPendingIdx)}
|
|
359
363
|
disabled={running}
|
|
364
|
+
title={running ? t.hints.workflowRunning : undefined}
|
|
360
365
|
style={{
|
|
361
366
|
display: 'flex', alignItems: 'center', gap: 5,
|
|
362
367
|
padding: '4px 12px', borderRadius: 7, fontSize: '0.75rem',
|
|
@@ -125,6 +125,7 @@ export function AiTab({ data, updateAi, updateAgent, t }: AiTabProps) {
|
|
|
125
125
|
<button
|
|
126
126
|
type="button"
|
|
127
127
|
disabled={disabled}
|
|
128
|
+
title={disabled ? t.hints.testInProgressOrNoKey : undefined}
|
|
128
129
|
onClick={() => handleTestKey(providerName)}
|
|
129
130
|
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:border-foreground/20 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
|
130
131
|
>
|
|
@@ -173,6 +173,7 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
|
|
|
173
173
|
<button
|
|
174
174
|
onClick={() => setShowCleanupConfirm(true)}
|
|
175
175
|
disabled={cleaningUp}
|
|
176
|
+
title={cleaningUp ? t.hints.cleanupInProgress : undefined}
|
|
176
177
|
className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors shrink-0 disabled:opacity-50"
|
|
177
178
|
>
|
|
178
179
|
{cleaningUp ? <Loader2 size={12} className="animate-spin" /> : <Trash2 size={12} />}
|
|
@@ -249,6 +250,7 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
|
|
|
249
250
|
type="button"
|
|
250
251
|
onClick={handleResetToken}
|
|
251
252
|
disabled={resetting}
|
|
253
|
+
title={resetting ? t.hints.tokenResetInProgress : undefined}
|
|
252
254
|
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
|
253
255
|
>
|
|
254
256
|
<RefreshCw size={12} className={resetting ? 'animate-spin' : ''} />
|
|
@@ -302,6 +302,7 @@ export function SyncTab({ t }: SyncTabProps) {
|
|
|
302
302
|
type="button"
|
|
303
303
|
onClick={handleSyncNow}
|
|
304
304
|
disabled={syncing}
|
|
305
|
+
title={syncing ? t.hints.syncInProgress : undefined}
|
|
305
306
|
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
|
306
307
|
>
|
|
307
308
|
<RefreshCw size={12} className={syncing ? 'animate-spin' : ''} />
|
|
@@ -311,6 +312,7 @@ export function SyncTab({ t }: SyncTabProps) {
|
|
|
311
312
|
type="button"
|
|
312
313
|
onClick={handleToggle}
|
|
313
314
|
disabled={toggling}
|
|
315
|
+
title={toggling ? t.hints.toggleInProgress : undefined}
|
|
314
316
|
className={`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg border transition-colors disabled:opacity-40 disabled:cursor-not-allowed ${
|
|
315
317
|
status.enabled
|
|
316
318
|
? 'border-border text-muted-foreground hover:text-destructive hover:border-destructive/50'
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { useLocale } from '@/lib/LocaleContext';
|
|
4
|
+
|
|
3
5
|
export interface StepDotsProps {
|
|
4
6
|
step: number;
|
|
5
7
|
setStep: (s: number) => void;
|
|
@@ -8,6 +10,7 @@ export interface StepDotsProps {
|
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export default function StepDots({ step, setStep, stepTitles, disabled }: StepDotsProps) {
|
|
13
|
+
const { t } = useLocale();
|
|
11
14
|
return (
|
|
12
15
|
<div className="flex items-center gap-2 mb-8" role="navigation" aria-label="Setup steps">
|
|
13
16
|
{stepTitles.map((title: string, i: number) => (
|
|
@@ -17,7 +20,8 @@ export default function StepDots({ step, setStep, stepTitles, disabled }: StepDo
|
|
|
17
20
|
aria-current={i === step ? 'step' : undefined}
|
|
18
21
|
aria-label={title}
|
|
19
22
|
className="flex flex-col items-center gap-1 p-1 -m-1 disabled:cursor-not-allowed disabled:opacity-60"
|
|
20
|
-
disabled={disabled || i >= step}
|
|
23
|
+
disabled={disabled || i >= step}
|
|
24
|
+
title={(disabled || i >= step) ? t.hints.cannotJumpForward : undefined}>
|
|
21
25
|
<div
|
|
22
26
|
className="w-6 h-6 rounded-full text-xs font-medium flex items-center justify-center transition-colors"
|
|
23
27
|
style={{
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mindos
|
|
3
|
+
description: >
|
|
4
|
+
Operate a MindOS knowledge base: update notes, search, organize files, execute SOPs/workflows,
|
|
5
|
+
retrospective, append CSV, cross-agent handoff, route unstructured input to the right files,
|
|
6
|
+
distill experience, sync related docs. 更新笔记, 搜索知识库, 整理, 执行SOP, 复盘, 追加CSV, 交接, 路由到文件, 提炼经验.
|
|
7
|
+
Use when the task targets files inside the user's MindOS KB (mindRoot).
|
|
8
|
+
NOT for editing app source, project docs, or paths outside the KB.
|
|
9
|
+
Core concepts: Space, Instruction (INSTRUCTION.md), Skill (SKILL.md); notes can embody both.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# MindOS Skill
|
|
13
|
+
|
|
14
|
+
<!-- version: 1.3.0 -->
|
|
15
|
+
|
|
16
|
+
**Before every task, internalize these 5 rules:**
|
|
17
|
+
|
|
18
|
+
1. The **bootstrap directory tree is the primary index** — reason from names and hierarchy before calling search. Most questions can be answered by reading what's already in context.
|
|
19
|
+
2. **Default to read-only.** Only invoke write tools when the user explicitly asks to save, record, organize, or edit. Lookup / summarize / quote = no writes.
|
|
20
|
+
3. **Rule precedence** (highest wins): user's current-turn instruction → `user-skill-rules.md` → nearest directory `INSTRUCTION.md` → root `INSTRUCTION.md` → this SKILL's defaults.
|
|
21
|
+
4. **Multi-file edits require a plan first.** Present the full change list; execute only after approval.
|
|
22
|
+
5. After create/delete/move/rename → **sync affected READMEs** automatically.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## NEVER do (hard-won pitfalls)
|
|
27
|
+
|
|
28
|
+
- **NEVER write to the KB root** unless the user explicitly says so. Root is for governance files only (`README.md`, `INSTRUCTION.md`, `CONFIG`). New content goes under the most semantically fitting subdirectory.
|
|
29
|
+
- **NEVER assume directory names.** Don't hardcode `Workflows/`, `Projects/`, `Contacts/` — always infer from the bootstrap tree you actually received. The user's KB may use Chinese names, flat layout, or unconventional hierarchy.
|
|
30
|
+
- **NEVER use `mindos_write_file` for a small edit.** Use `update_section`, `update_lines`, or `insert_after_heading` — full-file rewrites destroy git diffs and make changes unauditable.
|
|
31
|
+
- **NEVER search with a single keyword.** Always fire 2-4 parallel searches (synonyms, abbreviations, Chinese/English variants). One keyword misses too much.
|
|
32
|
+
- **NEVER modify `INSTRUCTION.md` or `README.md` without confirmation.** These are governance docs — treat as high-sensitivity even for trivial-looking typo fixes.
|
|
33
|
+
- **NEVER create a file without checking siblings first.** Read at least 1-2 files in the target directory to learn local naming, heading style, and CSV schema. Inventing a new convention is a common cause of inconsistency.
|
|
34
|
+
- **NEVER leave orphan references.** After any rename/move, run `get_backlinks` and update every referring file. Missing this is the #1 source of broken links in a KB.
|
|
35
|
+
- **NEVER skip the routing confirmation for multi-file writes.** Even when the destinations seem obvious — the user's mental model may differ from yours.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## MindOS concepts
|
|
40
|
+
|
|
41
|
+
- **Space** — Knowledge partitions organized the way you think. Agents follow the same structure.
|
|
42
|
+
- **Instruction** — A rules file all connected agents obey. Written once, enforced everywhere.
|
|
43
|
+
- **Skill** — Teaches agents how to read, write, and organize the KB. Agents execute installed Skills, not guesses.
|
|
44
|
+
|
|
45
|
+
**Notes as Instruction and Skill** — `INSTRUCTION.md` and `SKILL.md` are just Markdown files in the tree. A note can be free-form text, governance rules agents must follow, or a procedure package agents execute.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Thinking framework
|
|
50
|
+
|
|
51
|
+
Before acting, ask yourself:
|
|
52
|
+
|
|
53
|
+
1. **What is the user's intent category?** → read-only lookup | single-file edit | multi-file routing | structural change | SOP execution. This determines which path to take below.
|
|
54
|
+
2. **Where does this content belong?** → Scan the directory tree. If you can't place it in <5 seconds of looking at names, the user probably needs to confirm.
|
|
55
|
+
3. **What already exists nearby?** → Read 1-2 sibling files before writing. Match their style.
|
|
56
|
+
4. **What will break if I change this?** → For renames/moves: `get_backlinks`. For content edits: think about who else cites this fact.
|
|
57
|
+
5. **Am I being asked, or am I volunteering?** → If the user didn't ask you to write, don't write.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Task routing decision tree
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
User request
|
|
65
|
+
│
|
|
66
|
+
├─ Only asks to look up / summarize / quote?
|
|
67
|
+
│ └─ YES → [Read-only path]: search + read + cite sources. No writes. Skip hooks.
|
|
68
|
+
│
|
|
69
|
+
├─ Asks to save / record / update / organize specific content?
|
|
70
|
+
│ ├─ Single file target? → [Single-file edit]: startup → read → minimal edit → verify
|
|
71
|
+
│ └─ Multiple files or unclear target? → [Multi-file routing]: parse → plan → confirm → edit
|
|
72
|
+
│
|
|
73
|
+
├─ Structural change (rename / move / delete / reorganize)?
|
|
74
|
+
│ └─ [Structural path]: get_backlinks → impact report → confirm → execute → update refs → sync READMEs
|
|
75
|
+
│
|
|
76
|
+
├─ Procedural / repeatable task?
|
|
77
|
+
│ └─ [SOP path]: search for matching SOP (keywords + tree scan) → read → execute stepwise → propose SOP update if diverged
|
|
78
|
+
│
|
|
79
|
+
├─ Retrospective / distill / handoff?
|
|
80
|
+
│ └─ [Retrospective path]: confirm scope → extract artifacts → route → summarize changes
|
|
81
|
+
│
|
|
82
|
+
└─ Ambiguous or too broad?
|
|
83
|
+
└─ ASK for clarification. Propose 2-3 specific options based on KB state. Do NOT start editing.
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Startup protocol (write tasks only)
|
|
89
|
+
|
|
90
|
+
Skip for [read-only path](#task-routing-decision-tree). For all writes:
|
|
91
|
+
|
|
92
|
+
1. **Bootstrap** — `mindos_bootstrap` (preferred) or manually read root `INSTRUCTION.md` + `README.md`.
|
|
93
|
+
2. **Discover structure** — `mindos_list_spaces` (top-level zones + README blurbs) and/or `mindos_list_files` + targeted `mindos_search_notes`. Never assume top-level names.
|
|
94
|
+
3. **Load local governance** — Read `README.md` / `INSTRUCTION.md` near the target path. Local conventions override global assumptions.
|
|
95
|
+
4. **Match existing SOP** — If the task is procedural: scan tree for a procedure-holding directory (names like `Workflows/`, `SOPs/`, `流程/` are hints — don't assume). Search by keywords + `<!-- keywords: -->` metadata. If found, read and follow. If execution diverges, propose updating the SOP after.
|
|
96
|
+
5. **Pre-write checks** — Confirm: target path exists or should be created; location is under a subdirectory (not root); current content is read; edit scope is minimal; backlink impact assessed for path changes.
|
|
97
|
+
6. **Execute edits.**
|
|
98
|
+
|
|
99
|
+
If context is missing, continue with best effort and state assumptions.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Tool selection
|
|
104
|
+
|
|
105
|
+
| Intent | Best tool | Avoid |
|
|
106
|
+
|--------|-----------|-------|
|
|
107
|
+
| Load context at start | `mindos_bootstrap` | Reading random files without bootstrap |
|
|
108
|
+
| List top-level Mind Spaces | `mindos_list_spaces` | Full `mindos_list_files` when you only need zone names and README blurbs |
|
|
109
|
+
| Find files | `mindos_search_notes` (2-4 parallel keyword variants) | Single-keyword search |
|
|
110
|
+
| Read content | `mindos_read_file` or `mindos_read_lines` (for large files) | Reading entire large file when you need 10 lines |
|
|
111
|
+
| Small text edit | `mindos_update_section` / `mindos_update_lines` / `mindos_insert_after_heading` | `mindos_write_file` for small changes |
|
|
112
|
+
| Append to end | `mindos_append_to_file` | Rewriting entire file to add a line |
|
|
113
|
+
| Full file replacement | `mindos_write_file` | Using this when a section edit suffices |
|
|
114
|
+
| New file | `mindos_create_file` | Creates parent dirs but does NOT scaffold Space files |
|
|
115
|
+
| New Mind Space (zone + README + INSTRUCTION) | `mindos_create_space` | The only way to create a Space. `create_file` creates plain folders |
|
|
116
|
+
| Rename a Space directory | `mindos_rename_space` | `rename_file` (files only; does not rename folders) |
|
|
117
|
+
| Add CSV row | `mindos_append_csv` (validates header) | Manual string append without header check |
|
|
118
|
+
| Check impact before rename | `mindos_get_backlinks` | Renaming without checking references |
|
|
119
|
+
| Inspect recent changes | `mindos_get_recent` | Guessing what changed recently |
|
|
120
|
+
| Recover old version | `mindos_get_file_at_version` | Asking user to recall what was there |
|
|
121
|
+
|
|
122
|
+
### Fallbacks
|
|
123
|
+
|
|
124
|
+
- `mindos_bootstrap` unavailable → manual reads of root `INSTRUCTION.md` + `README.md`.
|
|
125
|
+
- Line/section tools unavailable → read + constrained `mindos_write_file` (simulate minimal edit).
|
|
126
|
+
- Search returns empty → don't give up: (1) scan tree in context, (2) read candidate files directly, (3) `mindos_list_files` on specific subdirectories, (4) try synonym/alternate-language keywords.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Execution patterns
|
|
131
|
+
|
|
132
|
+
### Single-file edit
|
|
133
|
+
Search → read target + local conventions → minimal edit → verify style match → summarize.
|
|
134
|
+
|
|
135
|
+
### Context-aware Q&A (read-only)
|
|
136
|
+
Tree reasoning → search → read → answer with file citations → state gaps explicitly.
|
|
137
|
+
|
|
138
|
+
### Multi-file routing
|
|
139
|
+
Parse unstructured input into semantic units → search candidates per unit → **present routing table** (what → file → position) → confirm → edit → summarize all changes.
|
|
140
|
+
|
|
141
|
+
### Conversation retrospective
|
|
142
|
+
Confirm scope → extract decisions, rationale, pitfalls, next actions → route to best existing files → trace note of what changed.
|
|
143
|
+
|
|
144
|
+
### SOP execution
|
|
145
|
+
Read SOP fully → execute step by step → update only stale sections → if diverged, propose SOP update.
|
|
146
|
+
**When creating a new SOP** → read [references/sop-template.md](./references/sop-template.md) first.
|
|
147
|
+
|
|
148
|
+
### Structural changes
|
|
149
|
+
`get_backlinks` → report impact → confirm → execute → update all refs → sync READMEs.
|
|
150
|
+
|
|
151
|
+
### Quick reference patterns
|
|
152
|
+
|
|
153
|
+
| Pattern | Key steps |
|
|
154
|
+
|---------|-----------|
|
|
155
|
+
| CSV append | Read header → validate fields → `mindos_append_csv` |
|
|
156
|
+
| TODO management | Find list → read format → minimal edit preserving conventions |
|
|
157
|
+
| Cross-agent handoff | Read task state + decisions → continue without re-discovery → write back progress |
|
|
158
|
+
| Knowledge conflict | Multi-keyword search → list affected files → present change plan → confirm → update |
|
|
159
|
+
| Periodic review | `get_recent` / `get_history` → read changed files → structured summary |
|
|
160
|
+
| Handoff doc | Read sources → synthesize (background, decisions, status, open items) → place in project dir |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Interaction rules
|
|
165
|
+
|
|
166
|
+
- **Ambiguous request?** Ask first. Propose 2-3 options based on KB state (recent changes, directory structure). Never start reorganizing without understanding scope.
|
|
167
|
+
- **Cite sources.** Every fact from the KB gets a file path so the user can verify.
|
|
168
|
+
- **Brevity first.** Show the most likely match, not an exhaustive list.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Post-task hooks
|
|
173
|
+
|
|
174
|
+
After **write tasks**, read [references/post-task-hooks.md](./references/post-task-hooks.md) for one-line follow-up proposals (experience capture, consistency sync, SOP drift, etc.).
|
|
175
|
+
**Read-only tasks: skip hooks.** If the user asked for quiet mode, skip all hooks for the session.
|
|
176
|
+
**Do NOT read** post-task-hooks for simple single-file edits or read-only lookups.
|
|
177
|
+
|
|
178
|
+
## Preference capture
|
|
179
|
+
|
|
180
|
+
When the user expresses a standing preference ("don't do X", "always put Y in Z"), read [references/preference-capture.md](./references/preference-capture.md) and follow the confirm-then-write flow to `user-skill-rules.md`.
|
|
181
|
+
**Do NOT read** preference-capture unless the user actually expressed a preference to persist.
|
|
182
|
+
|
|
183
|
+
## SOP authoring
|
|
184
|
+
|
|
185
|
+
When creating or rewriting a workflow SOP, **MANDATORY — read [references/sop-template.md](./references/sop-template.md)** for required structure (prerequisites, steps with branches, exit conditions, pitfall log).
|
|
186
|
+
**Do NOT read** sop-template for SOP execution (only for SOP creation/editing).
|