@geminilight/mindos 0.5.2 → 0.5.5
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/app/app/api/mcp/agents/route.ts +1 -0
- package/app/app/api/mcp/install/route.ts +62 -7
- package/app/app/api/mcp/install-skill/route.ts +96 -0
- package/app/app/api/setup/route.ts +5 -2
- package/app/app/view/[...path]/ViewPageClient.tsx +4 -20
- package/app/components/SetupWizard.tsx +136 -16
- package/app/components/renderers/config/manifest.ts +1 -0
- package/app/components/renderers/csv/CsvRenderer.tsx +6 -12
- package/app/components/renderers/csv/manifest.ts +1 -0
- package/app/components/renderers/todo/manifest.ts +1 -0
- package/app/components/settings/McpTab.tsx +85 -7
- package/app/components/settings/PluginsTab.tsx +31 -16
- package/app/lib/i18n.ts +36 -6
- package/app/lib/mcp-agents.ts +10 -9
- package/app/lib/renderers/registry.ts +7 -0
- package/app/lib/renderers/useRendererState.ts +114 -0
- package/bin/cli.js +3 -1
- package/bin/lib/gateway.js +1 -1
- package/bin/lib/mcp-agents.js +9 -9
- package/package.json +1 -1
- package/scripts/setup.js +121 -18
- package/skills/human-insights/SKILL.md +0 -143
package/scripts/setup.js
CHANGED
|
@@ -133,6 +133,12 @@ const T = {
|
|
|
133
133
|
mcpInstallDone: { en: (n) => `✔ ${n} agent(s) configured`, zh: (n) => `✔ 已配置 ${n} 个 Agent` },
|
|
134
134
|
mcpSkipped: { en: ' → Skipped. Run `mindos mcp install` anytime to configure agents.', zh: ' → 已跳过。随时运行 `mindos mcp install` 配置 Agent。' },
|
|
135
135
|
|
|
136
|
+
// skill install step
|
|
137
|
+
skillInstalling: { en: (name) => `⏳ Installing Skill "${name}"...`, zh: (name) => `⏳ 正在安装 Skill "${name}"...` },
|
|
138
|
+
skillInstallOk: { en: (name) => ` ${c.green('✔')} Skill "${name}" installed`, zh: (name) => ` ${c.green('✔')} Skill "${name}" 已安装` },
|
|
139
|
+
skillInstallFail: { en: (name, msg) => ` ${c.red('✘')} Skill "${name}" failed: ${msg}`, zh: (name, msg) => ` ${c.red('✘')} Skill "${name}" 安装失败:${msg}` },
|
|
140
|
+
skillSkipped: { en: ' → No agents selected, skill install skipped.', zh: ' → 未选择 Agent,跳过 Skill 安装。' },
|
|
141
|
+
|
|
136
142
|
// restart prompts (re-onboard with config changes)
|
|
137
143
|
restartRequired: { en: 'Config changed. Service restart required.', zh: '配置已变更,需要重启服务。' },
|
|
138
144
|
restartNow: { en: 'Restart now?', zh: '立即重启?' },
|
|
@@ -441,8 +447,13 @@ async function isSelfPort(port) {
|
|
|
441
447
|
res.on('end', () => {
|
|
442
448
|
try {
|
|
443
449
|
const data = JSON.parse(body);
|
|
444
|
-
|
|
445
|
-
|
|
450
|
+
// 200 with service=mindos → definitely us.
|
|
451
|
+
// 401 Unauthorized → also us (webPassword is set).
|
|
452
|
+
resolve(data.service === 'mindos' || res.statusCode === 401);
|
|
453
|
+
} catch {
|
|
454
|
+
// Non-JSON but got a response on /api/health → likely us
|
|
455
|
+
resolve(res.statusCode === 401 || res.statusCode === 200);
|
|
456
|
+
}
|
|
446
457
|
});
|
|
447
458
|
});
|
|
448
459
|
req.on('error', () => resolve(false));
|
|
@@ -671,7 +682,7 @@ async function runMcpInstallStep(mcpPort, authToken) {
|
|
|
671
682
|
|
|
672
683
|
if (selected.length === 0) {
|
|
673
684
|
write(c.dim(t('mcpSkipped') + '\n'));
|
|
674
|
-
return;
|
|
685
|
+
return [];
|
|
675
686
|
}
|
|
676
687
|
|
|
677
688
|
write('\n' + c.dim(tf('mcpInstalling', selected.length) + '\n'));
|
|
@@ -705,6 +716,64 @@ async function runMcpInstallStep(mcpPort, authToken) {
|
|
|
705
716
|
}
|
|
706
717
|
|
|
707
718
|
console.log(`\n${c.green(tf('mcpInstallDone', okCount))}`);
|
|
719
|
+
return selected;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/* ── Skill auto-install ────────────────────────────────────────────────────── */
|
|
723
|
+
|
|
724
|
+
const UNIVERSAL_AGENTS = new Set([
|
|
725
|
+
'amp', 'cline', 'codex', 'cursor', 'gemini-cli',
|
|
726
|
+
'github-copilot', 'kimi-cli', 'opencode', 'warp',
|
|
727
|
+
]);
|
|
728
|
+
const SKILL_UNSUPPORTED = new Set(['claude-desktop']);
|
|
729
|
+
const AGENT_NAME_MAP = {
|
|
730
|
+
'claude-code': 'claude-code',
|
|
731
|
+
'windsurf': 'windsurf',
|
|
732
|
+
'trae': 'trae',
|
|
733
|
+
'openclaw': 'openclaw',
|
|
734
|
+
'codebuddy': 'codebuddy',
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Install the appropriate MindOS Skill to selected agents via `npx skills add`.
|
|
739
|
+
* @param {string} template - 'en' | 'zh' | 'empty' | 'custom'
|
|
740
|
+
* @param {string[]} selectedAgents - MCP agent keys from the multi-select step
|
|
741
|
+
*/
|
|
742
|
+
function runSkillInstallStep(template, selectedAgents) {
|
|
743
|
+
if (!selectedAgents || selectedAgents.length === 0) {
|
|
744
|
+
write(c.dim(t('skillSkipped') + '\n'));
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const skillName = template === 'zh' ? 'mindos-zh' : 'mindos';
|
|
749
|
+
const source = resolve(ROOT, 'skills');
|
|
750
|
+
|
|
751
|
+
// Filter to non-universal, skill-capable agents
|
|
752
|
+
const additionalAgents = selectedAgents
|
|
753
|
+
.filter(key => !UNIVERSAL_AGENTS.has(key) && !SKILL_UNSUPPORTED.has(key))
|
|
754
|
+
.map(key => AGENT_NAME_MAP[key] || key);
|
|
755
|
+
|
|
756
|
+
let cmd;
|
|
757
|
+
if (additionalAgents.length > 0) {
|
|
758
|
+
cmd = `npx skills add "${source}" -s ${skillName} -a ${additionalAgents.join(',')} -g -y`;
|
|
759
|
+
} else {
|
|
760
|
+
cmd = `npx skills add "${source}" -s ${skillName} -a universal -g -y`;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
write(tf('skillInstalling', skillName) + '\n');
|
|
764
|
+
|
|
765
|
+
try {
|
|
766
|
+
execSync(cmd, {
|
|
767
|
+
encoding: 'utf-8',
|
|
768
|
+
timeout: 30_000,
|
|
769
|
+
env: { ...process.env, NODE_ENV: 'production' },
|
|
770
|
+
stdio: 'pipe',
|
|
771
|
+
});
|
|
772
|
+
write(tf('skillInstallOk', skillName) + '\n');
|
|
773
|
+
} catch (err) {
|
|
774
|
+
const msg = err.stderr || err.message || 'Unknown error';
|
|
775
|
+
write(tf('skillInstallFail', skillName, msg.split('\n')[0]) + '\n');
|
|
776
|
+
}
|
|
708
777
|
}
|
|
709
778
|
|
|
710
779
|
// ── GUI Setup ─────────────────────────────────────────────────────────────────
|
|
@@ -739,16 +808,34 @@ async function startGuiSetup() {
|
|
|
739
808
|
// Read or create config, set setupPending
|
|
740
809
|
let config = {};
|
|
741
810
|
try { config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch { /* ignore */ }
|
|
811
|
+
|
|
812
|
+
const isFirstTime = !config.mindRoot;
|
|
742
813
|
config.setupPending = true;
|
|
743
814
|
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
|
|
744
815
|
|
|
745
|
-
//
|
|
746
|
-
|
|
747
|
-
if (
|
|
748
|
-
|
|
749
|
-
|
|
816
|
+
// Determine which port to use for the setup wizard
|
|
817
|
+
let usePort;
|
|
818
|
+
if (isFirstTime) {
|
|
819
|
+
// First-time onboard: use a temporary port (scan from 9100) so the user's
|
|
820
|
+
// chosen port in Step 3 can differ without a mid-setup restart.
|
|
821
|
+
// 9100 is chosen to avoid conflicts with common services (5000=AirPlay, 3000/8080=dev).
|
|
822
|
+
usePort = await findFreePort(9100);
|
|
823
|
+
} else {
|
|
824
|
+
// Re-onboard: service is already running on config.port — reuse it.
|
|
825
|
+
const existingPort = config.port || 3000;
|
|
826
|
+
if (await isSelfPort(existingPort)) {
|
|
827
|
+
// Service already running — just open the setup page, no need to spawn.
|
|
828
|
+
const url = `http://localhost:${existingPort}/setup`;
|
|
829
|
+
console.log(`\n${c.green(tf('guiReady', url))}\n`);
|
|
830
|
+
const opened = openBrowser(url);
|
|
831
|
+
if (!opened) console.log(c.dim(tf('guiOpenFailed', url)));
|
|
832
|
+
process.exit(0);
|
|
833
|
+
}
|
|
834
|
+
// Service not running — start on existing port
|
|
835
|
+
usePort = await isPortInUse(existingPort)
|
|
836
|
+
? await findFreePort(9100) // existing port occupied by another process
|
|
837
|
+
: existingPort;
|
|
750
838
|
}
|
|
751
|
-
const usePort = config.port || port;
|
|
752
839
|
|
|
753
840
|
write(c.yellow(t('guiStarting') + '\n'));
|
|
754
841
|
|
|
@@ -841,6 +928,15 @@ async function main() {
|
|
|
841
928
|
|
|
842
929
|
const { readdirSync } = await import('node:fs');
|
|
843
930
|
let mindDir;
|
|
931
|
+
let selectedTemplate = 'en'; // hoisted — set by template selection or inferred from existing config
|
|
932
|
+
// Infer template from existing config's disabledSkills or UI language
|
|
933
|
+
if (resumeCfg.disabledSkills?.includes('mindos')) {
|
|
934
|
+
selectedTemplate = 'zh';
|
|
935
|
+
} else if (resumeCfg.disabledSkills?.includes('mindos-zh')) {
|
|
936
|
+
selectedTemplate = 'en';
|
|
937
|
+
} else {
|
|
938
|
+
selectedTemplate = uiLang; // fallback to UI language for first-time existing KB
|
|
939
|
+
}
|
|
844
940
|
|
|
845
941
|
// Default KB path: existing mindRoot if set, otherwise ~/MindOS (same as GUI default)
|
|
846
942
|
const HOME = homedir();
|
|
@@ -875,9 +971,9 @@ async function main() {
|
|
|
875
971
|
} else {
|
|
876
972
|
// ── Template selection (part of Step 1) ─────────────────────────────
|
|
877
973
|
write('\n');
|
|
878
|
-
|
|
974
|
+
selectedTemplate = await select('tplPrompt', 'tplOptions', 'tplValues');
|
|
879
975
|
mkdirSync(mindDir, { recursive: true });
|
|
880
|
-
await applyTemplate(
|
|
976
|
+
await applyTemplate(selectedTemplate, mindDir);
|
|
881
977
|
break;
|
|
882
978
|
}
|
|
883
979
|
}
|
|
@@ -1000,12 +1096,13 @@ async function main() {
|
|
|
1000
1096
|
}
|
|
1001
1097
|
|
|
1002
1098
|
const config = {
|
|
1003
|
-
mindRoot:
|
|
1004
|
-
port:
|
|
1005
|
-
mcpPort:
|
|
1006
|
-
authToken:
|
|
1007
|
-
webPassword:
|
|
1008
|
-
startMode:
|
|
1099
|
+
mindRoot: mindDir,
|
|
1100
|
+
port: webPort,
|
|
1101
|
+
mcpPort: mcpPort,
|
|
1102
|
+
authToken: authToken,
|
|
1103
|
+
webPassword: webPassword || '',
|
|
1104
|
+
startMode: startMode,
|
|
1105
|
+
disabledSkills: selectedTemplate === 'zh' ? ['mindos'] : ['mindos-zh'],
|
|
1009
1106
|
ai: {
|
|
1010
1107
|
provider: isSkip ? existingAiProvider : (isAnthropic ? 'anthropic' : 'openai'),
|
|
1011
1108
|
providers: existingProviders,
|
|
@@ -1052,7 +1149,13 @@ async function main() {
|
|
|
1052
1149
|
stepHeader(7);
|
|
1053
1150
|
write(c.dim(tf('mcpStepHint') + '\n\n'));
|
|
1054
1151
|
|
|
1055
|
-
await runMcpInstallStep(mcpPort, authToken);
|
|
1152
|
+
const selectedAgents = await runMcpInstallStep(mcpPort, authToken);
|
|
1153
|
+
|
|
1154
|
+
// ── Skill auto-install ────────────────────────────────────────────────────
|
|
1155
|
+
if (selectedAgents && selectedAgents.length > 0) {
|
|
1156
|
+
write('\n');
|
|
1157
|
+
runSkillInstallStep(selectedTemplate, selectedAgents);
|
|
1158
|
+
}
|
|
1056
1159
|
|
|
1057
1160
|
// ── Sync setup (optional) ──────────────────────────────────────────────────
|
|
1058
1161
|
const wantSync = await askYesNo('syncSetup');
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: human-insights
|
|
3
|
-
description: "捕捉、记录和提炼人机协作过程中产生的隐性知识,存入项目 human-insights/ 目录。当用户说「记录下这个发现」「这个方法很有效,存一下」「回顾下我们的协作」「总结下 SOP」「这个 AI 总是犯这个错」「把这个变成规则」「整理下 insights」「提炼下规律」「升华到 CLAUDE.md」时使用此 Skill。对话中出现值得沉淀的协作模式、认知升级、反模式时也应触发。"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Human Insights Skill
|
|
7
|
-
|
|
8
|
-
捕捉人机协作中产生的隐性知识,让它不随对话消失,并定期提炼为可复用的 SOP。
|
|
9
|
-
|
|
10
|
-
核心设计原则:**零摩擦**。记录的成本越低,记录的频率越高,积累的价值越大。所有让用户需要思考"怎么配合"的设计都是反模式。
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## 核心理念
|
|
15
|
-
|
|
16
|
-
insights 不是终点,是 CLAUDE.md 的原材料。生命周期:
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
对话碎片 → 捕捉 → human-insights/ → 定期回顾提炼 → CLAUDE.md / Skill
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
已升华进 CLAUDE.md 的 insight 标记为 `[已采纳]`,避免重复处理。
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 三种模式
|
|
27
|
-
|
|
28
|
-
### 模式一:捕捉(用户主动触发)
|
|
29
|
-
|
|
30
|
-
用户说"记录下这个"、"这个很有用"时触发。
|
|
31
|
-
|
|
32
|
-
1. 理解内容 — 读懂要记录什么,必要时追问一句
|
|
33
|
-
2. **AI 判断分类**,用户不需要选(见下方分类体系)
|
|
34
|
-
3. 检查目标文件,避免重复记录
|
|
35
|
-
4. 写入,自动填写时间,来源一句话
|
|
36
|
-
5. 告知用户记录到了哪里
|
|
37
|
-
|
|
38
|
-
### 模式二:主动提议(AI 识别信号)
|
|
39
|
-
|
|
40
|
-
对话中出现以下信号时,AI 在回答完后顺带问一句"这个发现值得记进 human-insights,要我记一下吗?"
|
|
41
|
-
|
|
42
|
-
**值得提议的信号(需同时满足:有新意 + 可复用):**
|
|
43
|
-
- 用户明确纠正了 AI 的错误认知或行为模式
|
|
44
|
-
- 用户发现某个做法比之前的方式明显更高效,且说清楚了为什么
|
|
45
|
-
- 对话中归纳出了一条可以跨场景复用的规律或原则
|
|
46
|
-
|
|
47
|
-
**不需要提议的情况:**
|
|
48
|
-
- 只是某个任务执行得顺利(不代表有可复用的模式)
|
|
49
|
-
- 用户已经在说"记一下"(模式一已覆盖)
|
|
50
|
-
- 同一对话中已经提议过一次(避免反复打断)
|
|
51
|
-
|
|
52
|
-
用户说"好"/"记一下"即执行,说"不用"则跳过。不打断主流程,轻量提议。
|
|
53
|
-
|
|
54
|
-
### 模式三:回顾提炼(定期整理)
|
|
55
|
-
|
|
56
|
-
用户说"回顾下协作"、"总结 SOP"、"升华到 CLAUDE.md" 时触发。
|
|
57
|
-
|
|
58
|
-
1. 读取 `human-insights/` 下所有文件,统计各类别条目数和"待提炼"数量
|
|
59
|
-
2. 识别模式 — 找出:① 反复出现的主题 ② 相互印证的发现 ③ 已有足够证据支撑的规律
|
|
60
|
-
3. 起草提炼结果,每条产出明确其形态:
|
|
61
|
-
- **规则**:适合写进 CLAUDE.md 的行为约束("做 X 之前先做 Y")
|
|
62
|
-
- **流程**:可复用的步骤序列(适合写成 Skill 或 CLAUDE.md 流程节)
|
|
63
|
-
- **Prompt 模板**:可直接复用的指令结构
|
|
64
|
-
4. 展示给用户确认,用户可修改或拒绝某条
|
|
65
|
-
5. 写入 CLAUDE.md 对应章节,原 insight 标记 `[已采纳 → CLAUDE.md #章节名]`
|
|
66
|
-
6. 更新 `README.md` 各文件摘要
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## 分类体系(AI 判断,用户无感)
|
|
71
|
-
|
|
72
|
-
| 类别 | 文件 | 记录什么 |
|
|
73
|
-
|------|------|---------|
|
|
74
|
-
| Prompt 模式 | `prompt-patterns.md` | 有效的提问方式、指令结构、上下文给法 |
|
|
75
|
-
| 工作流发现 | `workflow.md` | 有效的协作流程、任务拆分方式、节奏把握 |
|
|
76
|
-
| AI 行为规律 | `ai-behavior.md` | AI 在特定场景下的规律性表现、容易犯的错 |
|
|
77
|
-
| 认知升级 | `mindset.md` | 对某个概念/方法的新理解,之前认知被纠正 |
|
|
78
|
-
| 反模式 | `anti-patterns.md` | 踩过的坑、低效做法、应该避免的模式 |
|
|
79
|
-
|
|
80
|
-
一条 insight 可以属于多个类别时,以最主要的类别为准写入一个文件,不重复写入。
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## 记录格式
|
|
85
|
-
|
|
86
|
-
轻量为主,自动填时间,来源一句话够了:
|
|
87
|
-
|
|
88
|
-
```markdown
|
|
89
|
-
## <标题>
|
|
90
|
-
|
|
91
|
-
**时间:** YYYY-MM-DD **来源:** <一句话场景,如"MindOS CLAUDE.md 整理中">
|
|
92
|
-
|
|
93
|
-
<发现了什么、为什么有价值、怎么用。1-3 句话。>
|
|
94
|
-
|
|
95
|
-
**状态:** 待提炼 / [已采纳 → CLAUDE.md #章节名]
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## 目录结构
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
human-insights/
|
|
104
|
-
├── README.md # 索引 + 各文件一句话摘要(只列已创建的文件)
|
|
105
|
-
├── prompt-patterns.md
|
|
106
|
-
├── workflow.md
|
|
107
|
-
├── ai-behavior.md
|
|
108
|
-
├── mindset.md
|
|
109
|
-
└── anti-patterns.md
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
首次使用时只创建 `README.md` 和当前需要写入的文件,其余按需创建。
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## 初始化
|
|
117
|
-
|
|
118
|
-
`human-insights/` 不存在时自动创建目录和 `README.md`:
|
|
119
|
-
|
|
120
|
-
```markdown
|
|
121
|
-
# Human Insights
|
|
122
|
-
|
|
123
|
-
记录人机协作过程中产生的隐性知识,定期提炼为可复用的 SOP。
|
|
124
|
-
|
|
125
|
-
## 文件索引
|
|
126
|
-
|
|
127
|
-
(按需添加,创建新文件时同步更新此表)
|
|
128
|
-
|
|
129
|
-
| 文件 | 摘要 |
|
|
130
|
-
|------|------|
|
|
131
|
-
|
|
132
|
-
## 升华路径
|
|
133
|
-
|
|
134
|
-
积累 → 回顾提炼 → 写入 CLAUDE.md → 标记 [已采纳]
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## 边界说明
|
|
140
|
-
|
|
141
|
-
- vs `wiki/80-known-pitfalls.md`:pitfalls 记客观技术坑(别人也会踩),insights 记主观协作发现(你个人的认知)
|
|
142
|
-
- vs `wiki/06-conventions.md`:conventions 是已确定的规范,insights 是还在验证中的发现
|
|
143
|
-
- vs `CLAUDE.md`:CLAUDE.md 是已固化的规则,insights 是原材料
|