@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/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
- resolve(data.service === 'mindos');
445
- } catch { resolve(false); }
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
- // Find a free port
746
- const port = await findFreePort(3000);
747
- if (config.port === undefined) {
748
- config.port = port;
749
- writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
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
- const tpl = await select('tplPrompt', 'tplOptions', 'tplValues');
974
+ selectedTemplate = await select('tplPrompt', 'tplOptions', 'tplValues');
879
975
  mkdirSync(mindDir, { recursive: true });
880
- await applyTemplate(tpl, mindDir);
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: mindDir,
1004
- port: webPort,
1005
- mcpPort: mcpPort,
1006
- authToken: authToken,
1007
- webPassword: webPassword || '',
1008
- startMode: 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 是原材料