@geminilight/mindos 0.6.30 → 0.6.32

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 (52) hide show
  1. package/README_zh.md +10 -4
  2. package/app/app/api/ask/route.ts +12 -7
  3. package/app/app/api/export/route.ts +105 -0
  4. package/app/app/globals.css +2 -2
  5. package/app/app/trash/page.tsx +7 -0
  6. package/app/app/view/[...path]/ViewPageClient.tsx +234 -2
  7. package/app/components/ExportModal.tsx +220 -0
  8. package/app/components/FileTree.tsx +22 -2
  9. package/app/components/HomeContent.tsx +91 -20
  10. package/app/components/MarkdownView.tsx +45 -10
  11. package/app/components/Sidebar.tsx +10 -1
  12. package/app/components/TrashPageClient.tsx +263 -0
  13. package/app/components/ask/ToolCallBlock.tsx +102 -18
  14. package/app/components/changes/ChangesContentPage.tsx +58 -14
  15. package/app/components/explore/ExploreContent.tsx +4 -7
  16. package/app/components/explore/UseCaseCard.tsx +18 -1
  17. package/app/components/explore/use-cases.generated.ts +76 -0
  18. package/app/components/explore/use-cases.yaml +185 -0
  19. package/app/components/panels/DiscoverPanel.tsx +1 -1
  20. package/app/components/renderers/workflow-yaml/StepEditor.tsx +98 -91
  21. package/app/components/renderers/workflow-yaml/WorkflowEditor.tsx +72 -72
  22. package/app/components/renderers/workflow-yaml/WorkflowRunner.tsx +175 -119
  23. package/app/components/renderers/workflow-yaml/WorkflowYamlRenderer.tsx +61 -61
  24. package/app/components/renderers/workflow-yaml/execution.ts +64 -12
  25. package/app/components/renderers/workflow-yaml/selectors.tsx +65 -13
  26. package/app/components/settings/AiTab.tsx +191 -174
  27. package/app/components/settings/AppearanceTab.tsx +168 -77
  28. package/app/components/settings/KnowledgeTab.tsx +131 -136
  29. package/app/components/settings/McpTab.tsx +11 -11
  30. package/app/components/settings/Primitives.tsx +60 -0
  31. package/app/components/settings/SettingsContent.tsx +15 -8
  32. package/app/components/settings/SyncTab.tsx +12 -12
  33. package/app/components/settings/UninstallTab.tsx +8 -18
  34. package/app/components/settings/UpdateTab.tsx +82 -82
  35. package/app/components/settings/types.ts +17 -8
  36. package/app/lib/acp/session.ts +12 -3
  37. package/app/lib/actions.ts +57 -3
  38. package/app/lib/agent/stream-consumer.ts +18 -0
  39. package/app/lib/agent/tools.ts +56 -9
  40. package/app/lib/core/export.ts +116 -0
  41. package/app/lib/core/trash.ts +241 -0
  42. package/app/lib/fs.ts +47 -0
  43. package/app/lib/hooks/usePinnedFiles.ts +90 -0
  44. package/app/lib/i18n/generated/explore-i18n.generated.ts +138 -0
  45. package/app/lib/i18n/index.ts +3 -0
  46. package/app/lib/i18n/modules/knowledge.ts +120 -6
  47. package/app/lib/i18n/modules/onboarding.ts +2 -134
  48. package/app/lib/i18n/modules/settings.ts +12 -0
  49. package/app/package.json +8 -2
  50. package/app/scripts/generate-explore.ts +145 -0
  51. package/package.json +1 -1
  52. package/app/components/explore/use-cases.ts +0 -58
@@ -0,0 +1,76 @@
1
+ // ⚠️ AUTO-GENERATED — DO NOT EDIT. Source: components/explore/use-cases.yaml
2
+ // Run `npm run generate` to regenerate.
3
+
4
+ /** Capability axis — maps to product pillars */
5
+ export type UseCaseCategory = 'knowledge-management' | 'memory-sync' | 'auto-execute' | 'experience-evolution' | 'human-insights' | 'audit-control';
6
+
7
+ /** Scenario axis — maps to user journey phase */
8
+ export type UseCaseScenario = 'first-day' | 'daily' | 'project' | 'advanced';
9
+
10
+ export interface UseCase {
11
+ id: string;
12
+ icon: string;
13
+ image?: string;
14
+ category: UseCaseCategory;
15
+ scenario: UseCaseScenario;
16
+ }
17
+
18
+ export const useCases: UseCase[] = [
19
+ {
20
+ "id": "c1",
21
+ "icon": "👤",
22
+ "category": "memory-sync",
23
+ "scenario": "first-day"
24
+ },
25
+ {
26
+ "id": "c2",
27
+ "icon": "📥",
28
+ "category": "knowledge-management",
29
+ "scenario": "daily"
30
+ },
31
+ {
32
+ "id": "c3",
33
+ "icon": "🔄",
34
+ "category": "memory-sync",
35
+ "scenario": "project"
36
+ },
37
+ {
38
+ "id": "c4",
39
+ "icon": "🔁",
40
+ "category": "experience-evolution",
41
+ "scenario": "daily"
42
+ },
43
+ {
44
+ "id": "c5",
45
+ "icon": "💡",
46
+ "category": "auto-execute",
47
+ "scenario": "daily"
48
+ },
49
+ {
50
+ "id": "c6",
51
+ "icon": "🚀",
52
+ "category": "auto-execute",
53
+ "scenario": "project"
54
+ },
55
+ {
56
+ "id": "c7",
57
+ "icon": "🔍",
58
+ "category": "knowledge-management",
59
+ "scenario": "project"
60
+ },
61
+ {
62
+ "id": "c8",
63
+ "icon": "🤝",
64
+ "category": "human-insights",
65
+ "scenario": "daily"
66
+ },
67
+ {
68
+ "id": "c9",
69
+ "icon": "🛡️",
70
+ "category": "audit-control",
71
+ "scenario": "advanced"
72
+ }
73
+ ];
74
+
75
+ export const categories: UseCaseCategory[] = ["knowledge-management","memory-sync","auto-execute","experience-evolution","human-insights","audit-control"];
76
+ export const scenarios: UseCaseScenario[] = ["first-day","daily","project","advanced"];
@@ -0,0 +1,185 @@
1
+ # MindOS Explore — Use Case Definitions (Single Source of Truth)
2
+ #
3
+ # To add a new use case: just append a new entry below.
4
+ # Run `npm run generate` to regenerate use-cases.ts + i18n.
5
+ #
6
+ # Fields:
7
+ # id: Unique identifier (e.g. c10)
8
+ # icon: Emoji fallback when no image is available
9
+ # image: (optional) Custom image path under /explore/. Omit to use /explore/{id}.png
10
+ # category: One of: knowledge-management | memory-sync | auto-execute | experience-evolution | human-insights | audit-control
11
+ # scenario: One of: first-day | daily | project | advanced
12
+ # zh/en: Bilingual text — title, desc, prompt
13
+
14
+ # ── Category & Scenario labels (used by filter UI) ──
15
+ meta:
16
+ categories:
17
+ knowledge-management:
18
+ en: Knowledge Management
19
+ zh: 知识管理
20
+ memory-sync:
21
+ en: Memory Sync
22
+ zh: 记忆同步
23
+ auto-execute:
24
+ en: Auto Execute
25
+ zh: 自动执行
26
+ experience-evolution:
27
+ en: Experience Evolution
28
+ zh: 经验进化
29
+ human-insights:
30
+ en: Human Insights
31
+ zh: 人类洞察
32
+ audit-control:
33
+ en: Audit & Control
34
+ zh: 审计纠错
35
+ scenarios:
36
+ first-day:
37
+ en: First Day
38
+ zh: 初次使用
39
+ daily:
40
+ en: Daily Work
41
+ zh: 日常工作
42
+ project:
43
+ en: Project Work
44
+ zh: 项目协作
45
+ advanced:
46
+ en: Advanced
47
+ zh: 高级
48
+ ui:
49
+ title:
50
+ en: Explore Use Cases
51
+ zh: 探索使用场景
52
+ subtitle:
53
+ en: "Discover what you can do with MindOS \u2014 pick a scenario and try it now."
54
+ zh: "发现 MindOS 能帮你做什么 \u2014 选一个场景,立即体验。"
55
+ tryIt:
56
+ en: Try it
57
+ zh: 试一试
58
+ all:
59
+ en: All
60
+ zh: 全部
61
+ byCapability:
62
+ en: By Capability
63
+ zh: 按能力
64
+ byScenario:
65
+ en: By Scenario
66
+ zh: 按场景
67
+
68
+ # ── Use Cases ──
69
+ cases:
70
+ - id: c1
71
+ icon: "\U0001F464"
72
+ category: memory-sync
73
+ scenario: first-day
74
+ en:
75
+ title: Inject Your Identity
76
+ desc: "Tell all AI agents who you are \u2014 preferences, tech stack, communication style \u2014 in one shot."
77
+ prompt: "Here's my resume, read it and organize my info into MindOS."
78
+ zh:
79
+ title: 注入身份
80
+ desc: "让所有 AI Agent 一次认识你 \u2014 偏好、技术栈、沟通风格。"
81
+ prompt: 这是我的简历,读一下,把我的信息整理到 MindOS 里。
82
+
83
+ - id: c2
84
+ icon: "\U0001F4E5"
85
+ category: knowledge-management
86
+ scenario: daily
87
+ en:
88
+ title: Save Information
89
+ desc: Archive articles, meeting notes, or web pages into your knowledge base with one prompt.
90
+ prompt: Help me save the key points from this article into MindOS.
91
+ zh:
92
+ title: 注入信息
93
+ desc: 一句话归档文章、会议纪要或网页到知识库,全局可搜。
94
+ prompt: 帮我把这篇文章的要点整理到 MindOS 里。
95
+
96
+ - id: c3
97
+ icon: "\U0001F504"
98
+ category: memory-sync
99
+ scenario: project
100
+ en:
101
+ title: Cross-Agent Handoff
102
+ desc: "Start a plan in MindOS, continue coding in Claude Code, refine in Cursor \u2014 zero context loss."
103
+ prompt: Help me start coding based on the plan in MindOS.
104
+ zh:
105
+ title: 跨 Agent 切换
106
+ desc: "在 MindOS 写方案,在 Claude Code 写代码,在 Cursor 优化 \u2014 零重复。"
107
+ prompt: 帮我按 MindOS 里的 XXX 方案开始写代码。
108
+
109
+ - id: c4
110
+ icon: "\U0001F501"
111
+ category: experience-evolution
112
+ scenario: daily
113
+ en:
114
+ title: "Experience \u2192 SOP"
115
+ desc: Turn hard-won debugging sessions into reusable workflows that prevent future mistakes.
116
+ prompt: Help me distill this conversation into a reusable workflow in MindOS.
117
+ zh:
118
+ title: "经验\u2192SOP"
119
+ desc: 把踩坑经验沉淀为可复用的工作流,下次 3 分钟搞定。
120
+ prompt: 帮我把这次对话的经验沉淀到 MindOS,形成可复用的工作流。
121
+
122
+ - id: c5
123
+ icon: "\U0001F4A1"
124
+ category: auto-execute
125
+ scenario: daily
126
+ en:
127
+ title: Capture Ideas on the Go
128
+ desc: "Jot down an inspiration on your phone \u2014 MindOS archives, decomposes, and assigns to agents."
129
+ prompt: Help me organize this idea into MindOS and break it into actionable sub-tasks.
130
+ zh:
131
+ title: 手机记灵感
132
+ desc: 随手记下灵感,MindOS 自动归档、拆任务、多 Agent 接力执行。
133
+ prompt: 帮我把这个想法整理到 MindOS,拆解成可执行的子任务。
134
+
135
+ - id: c6
136
+ icon: "\U0001F680"
137
+ category: auto-execute
138
+ scenario: project
139
+ en:
140
+ title: Project Cold Start
141
+ desc: "Spin up a new project in 4 minutes \u2014 your profile and SOPs guide the scaffolding automatically."
142
+ prompt: Help me start a new project following the Startup SOP in MindOS.
143
+ zh:
144
+ title: 项目冷启动
145
+ desc: "4 分钟搭建新项目 \u2014 Profile 和 SOP 自动引导脚手架。"
146
+ prompt: 帮我按 MindOS 里的 Startup SOP 启动一个新项目。
147
+
148
+ - id: c7
149
+ icon: "\U0001F50D"
150
+ category: knowledge-management
151
+ scenario: project
152
+ en:
153
+ title: Research & Archive
154
+ desc: Let agents research competitors or topics for you, then file structured results in your KB.
155
+ prompt: "Help me research X, Y, Z products and save results to the MindOS product library."
156
+ zh:
157
+ title: 调研入库
158
+ desc: 让 Agent 替你跑腿调研竞品或话题,结果结构化入库。
159
+ prompt: 帮我调研 X、Y、Z 这几个产品,结果写入 MindOS 产品库。
160
+
161
+ - id: c8
162
+ icon: "\U0001F91D"
163
+ category: human-insights
164
+ scenario: daily
165
+ en:
166
+ title: Network Management
167
+ desc: "Log conversations with contacts, auto-generate follow-up TODOs, and keep full context."
168
+ prompt: "I met with someone today \u2014 update MindOS Connections and create follow-up TODOs."
169
+ zh:
170
+ title: 人脉管理
171
+ desc: 记录对话、自动生成跟进待办,每个联系人都有完整上下文。
172
+ prompt: 我今天和 XXX 聊了这些内容,帮我更新到 MindOS 并生成跟进待办。
173
+
174
+ - id: c9
175
+ icon: "\U0001F6E1\uFE0F"
176
+ category: audit-control
177
+ scenario: advanced
178
+ en:
179
+ title: Audit & Correct
180
+ desc: "Review what agents know about you, fix mistakes in one place, and all agents update instantly."
181
+ prompt: Check my MindOS Profile for accuracy and correct any errors.
182
+ zh:
183
+ title: 审计纠偏
184
+ desc: 审查 Agent 记了什么,一处修正,全局生效。
185
+ prompt: 帮我检查 MindOS Profile 里的技术栈偏好是否正确,有错误帮我修正。
@@ -6,7 +6,7 @@ import { Lightbulb, Blocks, Zap, LayoutTemplate, User, Download, RefreshCw, Repe
6
6
  import PanelHeader from './PanelHeader';
7
7
  import { PanelNavRow, ComingSoonBadge } from './PanelNavRow';
8
8
  import { useLocale } from '@/lib/LocaleContext';
9
- import { useCases } from '@/components/explore/use-cases';
9
+ import { useCases } from '@/components/explore/use-cases.generated';
10
10
  import { openAskModal } from '@/hooks/useAskModal';
11
11
  import { getPluginRenderers, isRendererEnabled, setRendererEnabled, loadDisabledState } from '@/lib/renderers/registry';
12
12
  import { Toggle } from '../settings/Primitives';
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState } from 'react';
4
- import { ChevronDown, ChevronUp, Trash2 } from 'lucide-react';
4
+ import { ChevronDown, ChevronUp, Trash2, Settings2 } from 'lucide-react';
5
5
  import { AgentSelector, ModelSelector, SkillsSelector, ContextSelector } from './selectors';
6
6
  import type { WorkflowStep } from './types';
7
7
 
@@ -15,142 +15,149 @@ interface StepEditorProps {
15
15
  }
16
16
 
17
17
  export default function StepEditor({ step, index, onChange, onDelete, onMoveUp, onMoveDown }: StepEditorProps) {
18
- // Auto-expand if step has no prompt (newly created)
19
18
  const [expanded, setExpanded] = useState(!step.prompt);
19
+ const [showConfig, setShowConfig] = useState(false);
20
20
 
21
21
  const update = (patch: Partial<WorkflowStep>) => onChange({ ...step, ...patch });
22
-
23
- // Merge legacy single skill into skills array for display
24
22
  const allSkills = step.skills?.length ? step.skills : (step.skill ? [step.skill] : []);
23
+ const hasConfig = !!(step.agent || step.model || allSkills.length || step.context?.length || step.description || step.timeout);
25
24
 
26
- // Collapsed view: summary line
25
+ // ── Collapsed ──
27
26
  if (!expanded) {
28
27
  return (
29
- <div className="group flex items-center gap-2 px-3 py-2.5 rounded-xl border border-border bg-card hover:border-[var(--amber)]/30 transition-colors cursor-pointer"
28
+ <div className="group flex items-start gap-2 px-3 py-2 rounded-lg hover:bg-muted/40 transition-colors cursor-pointer -ml-1"
30
29
  onClick={() => setExpanded(true)}>
31
- <span className="text-2xs text-muted-foreground/60 font-mono w-5 text-center shrink-0">{index + 1}</span>
32
- <span className={`text-sm font-medium truncate flex-1 ${step.name ? 'text-foreground' : 'text-muted-foreground italic'}`}>
33
- {step.name || 'Untitled step'}
34
- </span>
35
- <div className="flex items-center gap-1 shrink-0 flex-wrap justify-end">
36
- {allSkills.slice(0, 2).map(s => (
37
- <span key={s} className="text-2xs px-1.5 py-0.5 rounded bg-[var(--amber)]/10 text-[var(--amber)] border border-[var(--amber)]/20">{s}</span>
38
- ))}
39
- {allSkills.length > 2 && (
40
- <span className="text-2xs px-1 py-0.5 rounded bg-muted text-muted-foreground">+{allSkills.length - 2}</span>
30
+ <div className="min-w-0 flex-1">
31
+ <span className={`text-sm leading-tight block ${step.name ? 'text-foreground font-medium' : 'text-muted-foreground/50 italic'}`}>
32
+ {step.name || 'Untitled step'}
33
+ </span>
34
+ {/* Config summary pills */}
35
+ {hasConfig && (
36
+ <div className="flex items-center gap-1 mt-1 flex-wrap">
37
+ {step.agent && <span className="text-2xs px-1.5 py-0.5 rounded-full bg-muted text-muted-foreground">{step.agent}</span>}
38
+ {allSkills.slice(0, 2).map(s => (
39
+ <span key={s} className="text-2xs px-1.5 py-0.5 rounded-full bg-[var(--amber)]/8 text-[var(--amber)]">{s}</span>
40
+ ))}
41
+ {allSkills.length > 2 && <span className="text-2xs text-muted-foreground/50">+{allSkills.length - 2}</span>}
42
+ {step.context?.length ? <span className="text-2xs text-muted-foreground/50">{step.context.length} files</span> : null}
43
+ </div>
44
+ )}
45
+ {/* Prompt preview */}
46
+ {step.prompt && (
47
+ <p className="text-2xs text-muted-foreground/50 mt-1 line-clamp-1 leading-relaxed">{step.prompt}</p>
41
48
  )}
42
- {step.agent && <span className="text-2xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground">🤖 {step.agent}</span>}
43
- {step.agent && step.model && <span className="text-2xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground">🧠 {step.model}</span>}
44
- {step.context?.length ? <span className="text-2xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground">📎 {step.context.length}</span> : null}
45
49
  </div>
46
- <ChevronDown size={12} className="text-muted-foreground/50 shrink-0" />
50
+ <ChevronDown size={12} className="text-muted-foreground/30 mt-1 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity" />
47
51
  </div>
48
52
  );
49
53
  }
50
54
 
51
- // Expanded edit form
55
+ // ── Expanded ──
52
56
  return (
53
- <div className="rounded-xl border border-[var(--amber)]/30 bg-card overflow-hidden">
57
+ <div className="rounded-lg border border-[var(--amber)]/20 bg-card overflow-hidden shadow-sm">
54
58
  {/* Header */}
55
- <div className="flex items-center gap-2 px-3.5 py-2.5 border-b border-border bg-muted/30">
56
- <span className="text-2xs text-muted-foreground/60 font-mono w-5 text-center shrink-0">{index + 1}</span>
57
- <span className={`text-xs font-medium flex-1 truncate ${step.name ? 'text-foreground' : 'text-muted-foreground italic'}`}>
59
+ <div className="flex items-center gap-2 px-3 py-2 bg-muted/20">
60
+ <span className={`text-xs font-medium flex-1 truncate ${step.name ? 'text-foreground' : 'text-muted-foreground/50 italic'}`}>
58
61
  {step.name || 'Untitled step'}
59
62
  </span>
60
63
  <div className="flex items-center gap-0.5">
61
64
  {onMoveUp && (
62
- <button onClick={onMoveUp} className="p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title="Move up">
63
- <ChevronUp size={13} />
65
+ <button onClick={onMoveUp} className="p-1 rounded hover:bg-muted text-muted-foreground/50 hover:text-foreground transition-colors" title="Move up">
66
+ <ChevronUp size={12} />
64
67
  </button>
65
68
  )}
66
69
  {onMoveDown && (
67
- <button onClick={onMoveDown} className="p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title="Move down">
68
- <ChevronDown size={13} />
70
+ <button onClick={onMoveDown} className="p-1 rounded hover:bg-muted text-muted-foreground/50 hover:text-foreground transition-colors" title="Move down">
71
+ <ChevronDown size={12} />
69
72
  </button>
70
73
  )}
71
- <button onClick={onDelete} className="p-1 rounded hover:bg-[var(--error)]/10 text-muted-foreground hover:text-[var(--error)] transition-colors" title="Delete step">
72
- <Trash2 size={12} />
74
+ <button onClick={onDelete} className="p-1 rounded hover:bg-[var(--error)]/10 text-muted-foreground/50 hover:text-[var(--error)] transition-colors" title="Delete">
75
+ <Trash2 size={11} />
73
76
  </button>
74
- <button onClick={() => setExpanded(false)} className="p-1 rounded hover:bg-muted text-muted-foreground transition-colors" title="Collapse">
75
- <ChevronUp size={13} />
77
+ <button onClick={() => setExpanded(false)} className="p-1 rounded hover:bg-muted text-muted-foreground/50 transition-colors" title="Collapse">
78
+ <ChevronUp size={12} />
76
79
  </button>
77
80
  </div>
78
81
  </div>
79
82
 
80
- {/* Form body */}
81
- <div className="px-3.5 py-3 space-y-3">
82
- {/* Step name */}
83
- <div>
84
- <label className="block text-2xs font-medium text-muted-foreground mb-1">Step name</label>
85
- <input type="text" value={step.name} onChange={e => update({ name: e.target.value })}
86
- placeholder="e.g. Run Tests"
87
- autoFocus={!step.name}
88
- className="w-full px-2.5 py-1.5 text-sm rounded-md border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
89
- />
90
- </div>
83
+ {/* Body */}
84
+ <div className="px-3 py-3 space-y-3">
85
+ {/* Name inline style, not a labeled field */}
86
+ <input type="text" value={step.name} onChange={e => update({ name: e.target.value })}
87
+ placeholder="Step name..."
88
+ autoFocus={!step.name}
89
+ className="w-full text-sm font-medium bg-transparent text-foreground placeholder:text-muted-foreground/30 focus:outline-none border-none p-0"
90
+ />
91
91
 
92
- {/* Prompt */}
93
- <div>
94
- <label className="block text-2xs font-medium text-muted-foreground mb-1">Prompt</label>
95
- <textarea value={step.prompt} onChange={e => update({ prompt: e.target.value })}
96
- placeholder="Describe what the AI should do in this step..."
97
- rows={4}
98
- className="w-full px-2.5 py-1.5 text-sm rounded-md border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring resize-y leading-relaxed"
99
- />
100
- </div>
92
+ {/* Prompt — the main content */}
93
+ <textarea value={step.prompt} onChange={e => update({ prompt: e.target.value })}
94
+ placeholder="What should the AI do?"
95
+ rows={3}
96
+ className="w-full px-3 py-2.5 text-sm rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground/40 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring resize-y leading-relaxed"
97
+ />
101
98
 
102
- {/* Agent + Model */}
103
- <div className={`grid ${step.agent ? 'grid-cols-2' : 'grid-cols-1'} gap-3`}>
99
+ {/* Agent selector always visible, it's important */}
100
+ <div className={`grid ${step.agent ? 'grid-cols-2' : 'grid-cols-1'} gap-2`}>
104
101
  <div>
105
- <label className="block text-2xs font-medium text-muted-foreground mb-1">Agent</label>
102
+ <label className="block text-2xs text-muted-foreground/60 mb-1">Agent</label>
106
103
  <AgentSelector value={step.agent} onChange={agent => update({ agent, model: agent ? step.model : undefined })} />
107
104
  </div>
108
105
  {step.agent && (
109
106
  <div>
110
- <label className="block text-2xs font-medium text-muted-foreground mb-1">Model</label>
107
+ <label className="block text-2xs text-muted-foreground/60 mb-1">Model</label>
111
108
  <ModelSelector value={step.model} onChange={model => update({ model })} />
112
109
  </div>
113
110
  )}
114
111
  </div>
115
112
 
116
- {/* Skills (multi-select) */}
117
- <div>
118
- <label className="block text-2xs font-medium text-muted-foreground mb-1">Skills</label>
119
- <SkillsSelector
120
- value={allSkills}
121
- onChange={skills => update({ skills, skill: undefined })}
122
- />
123
- </div>
113
+ {/* Config toggle — progressive disclosure for skills, context, timeout */}
114
+ <button
115
+ onClick={() => setShowConfig(v => !v)}
116
+ className={`flex items-center gap-1.5 text-2xs transition-colors ${
117
+ showConfig || hasConfig ? 'text-muted-foreground' : 'text-muted-foreground/40 hover:text-muted-foreground'
118
+ }`}
119
+ >
120
+ <Settings2 size={11} />
121
+ {showConfig ? 'Less options' : 'More options'}
122
+ {!showConfig && hasConfig && <span className="w-1.5 h-1.5 rounded-full bg-[var(--amber)]" />}
123
+ </button>
124
124
 
125
- {/* Context files */}
126
- <div>
127
- <label className="block text-2xs font-medium text-muted-foreground mb-1">Context files</label>
128
- <ContextSelector
129
- value={step.context ?? []}
130
- onChange={context => update({ context: context.length ? context : undefined })}
131
- />
132
- </div>
125
+ {showConfig && (
126
+ <div className="space-y-3 pt-1">
127
+ {/* Skills */}
128
+ <div>
129
+ <label className="block text-2xs text-muted-foreground/60 mb-1">Skills</label>
130
+ <SkillsSelector value={allSkills} onChange={skills => update({ skills, skill: undefined })} />
131
+ </div>
133
132
 
134
- {/* Description + Timeout in one row */}
135
- <div className="grid grid-cols-[1fr,auto] gap-3">
136
- <div>
137
- <label className="block text-2xs font-medium text-muted-foreground mb-1">Description <span className="text-muted-foreground/50">(optional)</span></label>
138
- <input type="text" value={step.description || ''} onChange={e => update({ description: e.target.value || undefined })}
139
- placeholder="Brief description of this step"
140
- className="w-full px-2.5 py-1.5 text-xs rounded-md border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
141
- />
142
- </div>
143
- <div>
144
- <label className="block text-2xs font-medium text-muted-foreground mb-1">Timeout</label>
145
- <div className="flex items-center gap-1.5">
146
- <input type="number" min={0} value={step.timeout || ''} onChange={e => update({ timeout: e.target.value ? Number(e.target.value) : undefined })}
147
- placeholder="120"
148
- className="w-20 px-2.5 py-1.5 text-xs rounded-md border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
149
- />
150
- <span className="text-2xs text-muted-foreground/50">sec</span>
133
+ {/* Context files */}
134
+ <div>
135
+ <label className="block text-2xs text-muted-foreground/60 mb-1">Context files</label>
136
+ <ContextSelector value={step.context ?? []} onChange={context => update({ context: context.length ? context : undefined })} />
137
+ </div>
138
+
139
+ {/* Description + Timeout */}
140
+ <div className="grid grid-cols-[1fr,auto] gap-3">
141
+ <div>
142
+ <label className="block text-2xs text-muted-foreground/60 mb-1">Description</label>
143
+ <input type="text" value={step.description || ''} onChange={e => update({ description: e.target.value || undefined })}
144
+ placeholder="Optional note..."
145
+ className="w-full px-2.5 py-1.5 text-xs rounded-md border border-border bg-background text-foreground placeholder:text-muted-foreground/30 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
146
+ />
147
+ </div>
148
+ <div>
149
+ <label className="block text-2xs text-muted-foreground/60 mb-1">Timeout</label>
150
+ <div className="flex items-center gap-1">
151
+ <input type="number" min={0} value={step.timeout || ''} onChange={e => update({ timeout: e.target.value ? Number(e.target.value) : undefined })}
152
+ placeholder="120"
153
+ className="w-16 px-2 py-1.5 text-xs rounded-md border border-border bg-background text-foreground placeholder:text-muted-foreground/30 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
154
+ />
155
+ <span className="text-2xs text-muted-foreground/40">s</span>
156
+ </div>
157
+ </div>
151
158
  </div>
152
159
  </div>
153
- </div>
160
+ )}
154
161
  </div>
155
162
  </div>
156
163
  );