@geminilight/mindos 0.6.47 → 0.6.49

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 (152) hide show
  1. package/_standalone/.mindos-build-version +1 -1
  2. package/_standalone/.next/BUILD_ID +1 -1
  3. package/_standalone/.next/app-path-routes-manifest.json +23 -23
  4. package/_standalone/.next/build-manifest.json +2 -2
  5. package/_standalone/.next/cache/.previewinfo +1 -1
  6. package/_standalone/.next/cache/.rscinfo +1 -1
  7. package/_standalone/.next/cache/config.json +3 -3
  8. package/_standalone/.next/prerender-manifest.json +3 -3
  9. package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
  10. package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/_standalone/.next/server/app/_global-error.html +2 -2
  12. package/_standalone/.next/server/app/_global-error.rsc +1 -1
  13. package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  14. package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  15. package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  16. package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  17. package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  18. package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  19. package/_standalone/.next/server/app/_not-found/page.js +1 -1
  20. package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  21. package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  22. package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
  23. package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
  24. package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
  25. package/_standalone/.next/server/app/agents/page.js +1 -1
  26. package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
  27. package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  28. package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
  29. package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
  30. package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
  31. package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
  32. package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
  33. package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
  34. package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
  35. package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
  36. package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
  37. package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
  38. package/_standalone/.next/server/app/api/ask/route.js +23 -23
  39. package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
  40. package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
  41. package/_standalone/.next/server/app/api/auth/route.js +1 -1
  42. package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
  43. package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
  44. package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
  45. package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
  46. package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
  47. package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
  48. package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
  49. package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
  50. package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  51. package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  52. package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
  53. package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  54. package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
  55. package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  56. package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
  57. package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
  58. package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
  59. package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
  60. package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
  61. package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
  62. package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
  63. package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
  64. package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  65. package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  66. package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
  67. package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
  68. package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
  69. package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
  70. package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
  71. package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
  72. package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
  73. package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
  74. package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
  75. package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  76. package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
  77. package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
  78. package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
  79. package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  80. package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
  81. package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
  82. package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
  83. package/_standalone/.next/server/app/changes/page.js +1 -1
  84. package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
  85. package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
  86. package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
  87. package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
  88. package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
  89. package/_standalone/.next/server/app/echo/page.js +1 -1
  90. package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
  91. package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
  92. package/_standalone/.next/server/app/explore/page.js +1 -1
  93. package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
  94. package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
  95. package/_standalone/.next/server/app/help/page.js +1 -1
  96. package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
  97. package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
  98. package/_standalone/.next/server/app/login/page.js +1 -1
  99. package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
  100. package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  101. package/_standalone/.next/server/app/page.js +1 -1
  102. package/_standalone/.next/server/app/page.js.nft.json +1 -1
  103. package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  104. package/_standalone/.next/server/app/setup/page.js +2 -2
  105. package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
  106. package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  107. package/_standalone/.next/server/app/trash/page.js +2 -2
  108. package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
  109. package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
  110. package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
  111. package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
  112. package/_standalone/.next/server/app-paths-manifest.json +23 -23
  113. package/_standalone/.next/server/chunks/4241.js +25 -25
  114. package/_standalone/.next/server/chunks/{6946.js → 9986.js} +2 -2
  115. package/_standalone/.next/server/pages/500.html +2 -2
  116. package/_standalone/.next/server/server-reference-manifest.js +1 -1
  117. package/_standalone/.next/server/server-reference-manifest.json +1 -1
  118. package/_standalone/.next/static/chunks/{1053-98e7148893702bd2.js → 1053-8cb7fd1bfbbbedd3.js} +2 -2
  119. package/_standalone/.next/static/chunks/app/agents/{page-eac6c5f6650dbf62.js → page-0d9920b591ce999f.js} +1 -1
  120. package/_standalone/.next/static/chunks/app/echo/[segment]/{page-addf014fcf23fad5.js → page-88df174dd64b2c76.js} +1 -1
  121. package/_standalone/.next/static/chunks/app/{layout-5cbdae4ee15fb842.js → layout-e49b880ef457bb2c.js} +27 -27
  122. package/_standalone/.next/static/chunks/app/page-b3a7338f30362edb.js +7 -0
  123. package/_standalone/.next/static/chunks/app/setup/page-81c37de31c9a5beb.js +1 -0
  124. package/_standalone/.next/static/chunks/app/trash/{page-4e18cc618035d072.js → page-c8bf8845e81cf112.js} +1 -1
  125. package/_standalone/.next/static/chunks/app/view/[...path]/page-409e1b9baa22c66a.js +12 -0
  126. package/_standalone/.next/trace +63 -63
  127. package/_standalone/components/ask/ProviderModelCapsule.tsx +3 -1
  128. package/_standalone/components/settings/SettingsContent.tsx +1 -0
  129. package/_standalone/components/setup/StepAgents.tsx +24 -3
  130. package/_standalone/components/setup/StepReview.tsx +55 -15
  131. package/_standalone/hooks/useAcpDetection.ts +6 -0
  132. package/_standalone/hooks/useSettingsAiAvailable.ts +17 -11
  133. package/_standalone/lib/i18n/modules/onboarding.ts +14 -0
  134. package/_standalone/tsconfig.tsbuildinfo +1 -1
  135. package/app/app/api/ask/route.ts +53 -7
  136. package/app/app/api/auth/route.ts +3 -0
  137. package/app/components/ask/ProviderModelCapsule.tsx +3 -1
  138. package/app/components/settings/SettingsContent.tsx +1 -0
  139. package/app/components/setup/StepAgents.tsx +24 -3
  140. package/app/components/setup/StepReview.tsx +55 -15
  141. package/app/components/setup/index.tsx +44 -2
  142. package/app/hooks/useAcpDetection.ts +6 -0
  143. package/app/hooks/useSettingsAiAvailable.ts +17 -11
  144. package/app/lib/i18n/modules/onboarding.ts +14 -0
  145. package/bin/commands/file.js +16 -1
  146. package/package.json +1 -1
  147. package/scripts/build-runtime-archive.sh +3 -0
  148. package/_standalone/.next/static/chunks/app/page-30cf129e870aa3b2.js +0 -7
  149. package/_standalone/.next/static/chunks/app/setup/page-abad6be1750aba3e.js +0 -1
  150. package/_standalone/.next/static/chunks/app/view/[...path]/page-26775356655fa712.js +0 -12
  151. /package/_standalone/.next/static/{5-9HMGiRDVPCrib2GFNC6 → v1Q015tiqW8z0rkxr-Kpt}/_buildManifest.js +0 -0
  152. /package/_standalone/.next/static/{5-9HMGiRDVPCrib2GFNC6 → v1Q015tiqW8z0rkxr-Kpt}/_ssgManifest.js +0 -0
@@ -225,6 +225,46 @@ function readKnowledgeFile(filePath: string): { ok: boolean; content: string; tr
225
225
  }
226
226
  }
227
227
 
228
+ /**
229
+ * Resolve skill file from multiple fallback locations.
230
+ * Tries in order: app/data/skills → skills → {mindRoot}/.skills → ~/.mindos/skills
231
+ * Returns { path, result } where result is the file content or error.
232
+ */
233
+ function resolveSkillFile(
234
+ skillName: string,
235
+ projectRoot: string,
236
+ mindRoot: string,
237
+ ): { path: string; result: ReturnType<typeof readAbsoluteFile> } {
238
+ const locations = [
239
+ // Primary: bundled location (Desktop/production builds)
240
+ path.join(projectRoot, 'app', 'data', 'skills', skillName, 'SKILL.md'),
241
+ // Fallback: repository root (dev/CLI usage)
242
+ path.join(projectRoot, 'skills', skillName, 'SKILL.md'),
243
+ // Fallback: knowledge base custom skills
244
+ path.join(mindRoot, '.skills', skillName, 'SKILL.md'),
245
+ // Fallback: global user skills
246
+ path.join(process.env.HOME || '/root', '.mindos', 'skills', skillName, 'SKILL.md'),
247
+ ];
248
+
249
+ for (const absPath of locations) {
250
+ const result = readAbsoluteFile(absPath);
251
+ if (result.ok) {
252
+ return { path: absPath, result };
253
+ }
254
+ }
255
+
256
+ // All locations failed — return last error
257
+ return {
258
+ path: locations[locations.length - 1],
259
+ result: {
260
+ ok: false,
261
+ content: '',
262
+ truncated: false,
263
+ error: `Skill not found: tried ${locations.length} locations`,
264
+ },
265
+ };
266
+ }
267
+
228
268
  /**
229
269
  * In-memory cache for absolute file reads (SKILL.md, etc).
230
270
  * Keyed by absPath. Re-reads only when file mtime changes.
@@ -457,15 +497,21 @@ export async function POST(req: NextRequest) {
457
497
  // Auto-load skill + bootstrap context for each request.
458
498
  const isZh = serverSettings.disabledSkills?.includes('mindos') ?? false;
459
499
  const skillDirName = isZh ? 'mindos-zh' : 'mindos';
460
- const appDir = process.env.MINDOS_PROJECT_ROOT
461
- ? path.join(process.env.MINDOS_PROJECT_ROOT, 'app')
462
- : process.cwd();
463
- const skillPath = path.join(appDir, `data/skills/${skillDirName}/SKILL.md`);
464
- const skillWritePath = path.join(appDir, `data/skills/${skillDirName}/references/write-supplement.md`);
465
- const skill = readAbsoluteFile(skillPath);
500
+ const projectRoot = process.env.MINDOS_PROJECT_ROOT || path.resolve(process.cwd(), '..');
501
+ const mindRoot = getMindRoot();
502
+
503
+ // Resolve skill file from multiple fallback locations (handles Core Update scenarios)
504
+ const skillInfo = resolveSkillFile(skillDirName, projectRoot, mindRoot);
505
+ const skill = skillInfo.result;
506
+
507
+ // Resolve write-supplement from the same base directory as SKILL.md
508
+ const skillWriteDir = path.dirname(skillInfo.path);
509
+ const skillWritePath = path.join(skillWriteDir, 'references', 'write-supplement.md');
466
510
  const skillWrite = readAbsoluteFile(skillWritePath);
467
511
 
468
- console.log(`[ask] SKILL skill=${skill.ok}, write-supplement=${skillWrite.ok}`);
512
+ console.log(
513
+ `[ask] SKILL skill=${skill.ok} (${skillInfo.path}), write-supplement=${skillWrite.ok}`
514
+ );
469
515
 
470
516
  const userSkillRules = readKnowledgeFile('.mindos/user-preferences.md');
471
517
 
@@ -11,6 +11,9 @@ const ALLOWED_ORIGIN_PATTERNS = [
11
11
  /^https?:\/\/localhost(:\d+)?$/,
12
12
  /^https?:\/\/127\.0\.0\.1(:\d+)?$/,
13
13
  /^https?:\/\/\[::1\](:\d+)?$/,
14
+ /^https?:\/\/10\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?$/,
15
+ /^https?:\/\/172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}(:\d+)?$/,
16
+ /^https?:\/\/192\.168\.\d{1,3}\.\d{1,3}(:\d+)?$/,
14
17
  /^capacitor:\/\//,
15
18
  /^file:\/\//,
16
19
  ];
@@ -95,8 +95,10 @@ export default function ProviderModelCapsule({
95
95
  };
96
96
  doFetch();
97
97
  const onVisible = () => { if (document.visibilityState === 'visible') doFetch(); };
98
+ const onSettingsChanged = () => doFetch();
98
99
  document.addEventListener('visibilitychange', onVisible);
99
- return () => { cancelled = true; document.removeEventListener('visibilitychange', onVisible); };
100
+ window.addEventListener('mindos:settings-changed', onSettingsChanged);
101
+ return () => { cancelled = true; document.removeEventListener('visibilitychange', onVisible); window.removeEventListener('mindos:settings-changed', onSettingsChanged); };
100
102
  }, []);
101
103
 
102
104
  const defaultProvider = (settingsData?.ai?.provider && isProviderId(settingsData.ai.provider))
@@ -124,6 +124,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
124
124
  body: JSON.stringify({ ai: d.ai, agent: d.agent, mindRoot: d.mindRoot, webPassword: d.webPassword, authToken: d.authToken }),
125
125
  });
126
126
  setStatus('saved');
127
+ window.dispatchEvent(new Event('mindos:settings-changed'));
127
128
  setTimeout(() => setStatus('idle'), 2500);
128
129
  } catch {
129
130
  setStatus('error');
@@ -7,6 +7,16 @@ import {
7
7
  import { Field, Select } from '@/components/settings/Primitives';
8
8
  import type { SetupMessages, McpMessages, AgentEntry, AgentInstallStatus, ConnectionMode } from './types';
9
9
 
10
+ const AGENT_INSTALL_URLS: Record<string, string> = {
11
+ 'claude-code': 'https://docs.anthropic.com/en/docs/claude-code/overview',
12
+ 'cursor': 'https://www.cursor.com/',
13
+ 'windsurf': 'https://codeium.com/windsurf',
14
+ 'cline': 'https://github.com/cline/cline',
15
+ 'trae': 'https://www.trae.ai/',
16
+ 'gemini-cli': 'https://github.com/google-gemini/gemini-cli',
17
+ 'augment': 'https://www.augmentcode.com/',
18
+ };
19
+
10
20
  export interface StepAgentsProps {
11
21
  agents: AgentEntry[];
12
22
  agentsLoading: boolean;
@@ -79,10 +89,21 @@ export default function StepAgents({
79
89
  {s.agentDetected}
80
90
  </span>
81
91
  );
92
+ const installUrl = AGENT_INSTALL_URLS[key];
82
93
  return (
83
- <span className="text-xs px-1.5 py-0.5 rounded"
84
- style={{ background: 'color-mix(in srgb, var(--muted-foreground) 10%, transparent)', color: 'var(--muted-foreground)' }}>
85
- {s.agentNotFound}
94
+ <span className="flex items-center gap-1.5">
95
+ <span className="text-xs px-1.5 py-0.5 rounded"
96
+ style={{ background: 'color-mix(in srgb, var(--muted-foreground) 10%, transparent)', color: 'var(--muted-foreground)' }}>
97
+ {s.agentNotFound}
98
+ </span>
99
+ {installUrl && (
100
+ <a href={installUrl} target="_blank" rel="noopener noreferrer"
101
+ onClick={e => e.stopPropagation()}
102
+ className="text-2xs hover:underline"
103
+ style={{ color: 'var(--amber)' }}>
104
+ {s.agentGetIt}
105
+ </a>
106
+ )}
86
107
  </span>
87
108
  );
88
109
  };
@@ -3,7 +3,7 @@
3
3
  import { useState, useRef, useEffect, useCallback } from 'react';
4
4
  import {
5
5
  Loader2, AlertTriangle, CheckCircle2, XCircle, Copy, Check,
6
- FolderOpen, Brain, Plug, Shield,
6
+ FolderOpen, Brain, Plug, Shield, Sparkles,
7
7
  } from 'lucide-react';
8
8
  import { copyToClipboard } from '@/lib/clipboard';
9
9
  import { toast } from '@/lib/toast';
@@ -29,31 +29,52 @@ export function RestartBanner({ s }: { s: SetupMessages }) {
29
29
  }
30
30
 
31
31
  /** Restart button — shown in the bottom navigation bar (same position as Complete/Saving button) */
32
- export function RestartButton({ s, newPort }: { s: SetupMessages; newPort: number }) {
32
+ export function RestartButton({ s, newPort, webPassword }: { s: SetupMessages; newPort: number; webPassword?: string }) {
33
33
  const [restarting, setRestarting] = useState(false);
34
34
  const [done, setDone] = useState(false);
35
35
  const pollRef = useRef<ReturnType<typeof setInterval>>(undefined);
36
+ const delayRef = useRef<ReturnType<typeof setTimeout>>(undefined);
36
37
 
37
- // Cleanup polling interval on unmount
38
- useEffect(() => () => { clearInterval(pollRef.current); }, []);
38
+ useEffect(() => () => { clearTimeout(delayRef.current); clearInterval(pollRef.current); }, []);
39
39
 
40
40
  const handleRestart = async () => {
41
41
  setRestarting(true);
42
42
  try {
43
- await fetch('/api/restart', { method: 'POST' });
43
+ const restartRes = await fetch('/api/restart', { method: 'POST' });
44
+ if (!restartRes.ok) throw new Error(`restart failed (${restartRes.status})`);
44
45
  setDone(true);
45
- const redirect = () => { window.location.href = `http://localhost:${newPort}/?welcome=1`; };
46
- // Poll the new port until ready, then redirect
46
+ const rawHost = window.location.hostname || 'localhost';
47
+ const host = rawHost.includes(':') ? `[${rawHost}]` : rawHost;
48
+ const baseUrl = `http://${host}:${newPort}`;
49
+ const redirect = () => { window.location.href = `${baseUrl}/?welcome=1`; };
50
+
47
51
  let attempts = 0;
48
52
  clearInterval(pollRef.current);
49
- pollRef.current = setInterval(async () => {
53
+ // Delay first poll to ensure the old server has been killed by `mindos restart`
54
+ const startPoll = () => { pollRef.current = setInterval(async () => {
50
55
  attempts++;
51
56
  try {
52
- const r = await fetch(`http://localhost:${newPort}/api/health`);
53
- if (r.status < 500) { clearInterval(pollRef.current); redirect(); return; }
57
+ const r = await fetch(`${baseUrl}/api/health`);
58
+ if (r.status < 500) {
59
+ clearInterval(pollRef.current);
60
+ // Auto-authenticate so the user doesn't have to re-enter their password
61
+ if (webPassword) {
62
+ try {
63
+ await fetch(`${baseUrl}/api/auth`, {
64
+ method: 'POST',
65
+ headers: { 'Content-Type': 'application/json' },
66
+ body: JSON.stringify({ password: webPassword }),
67
+ credentials: 'include',
68
+ });
69
+ } catch { /* auth failed — user will see login page instead */ }
70
+ }
71
+ redirect();
72
+ return;
73
+ }
54
74
  } catch { /* not ready yet */ }
55
- if (attempts >= 10) { clearInterval(pollRef.current); redirect(); }
56
- }, 800);
75
+ if (attempts >= 30) { clearInterval(pollRef.current); redirect(); }
76
+ }, 800); };
77
+ delayRef.current = setTimeout(startPoll, 2000);
57
78
  } catch (e) {
58
79
  console.warn('[SetupWizard] restart request failed:', e);
59
80
  setRestarting(false);
@@ -91,14 +112,15 @@ export interface StepReviewProps {
91
112
  error: string;
92
113
  needsRestart: boolean;
93
114
  s: SetupMessages;
94
- setupPhase: 'review' | 'saving' | 'agents' | 'done';
115
+ setupPhase: 'review' | 'saving' | 'agents' | 'skills' | 'done';
95
116
  cliEnabled: boolean;
96
117
  mcpEnabled: boolean;
118
+ skillInstallStatus?: 'pending' | 'installing' | 'ok' | 'error' | 'skipped';
97
119
  }
98
120
 
99
121
  export default function StepReview({
100
122
  state, selectedAgents, agentStatuses, onRetryAgent, error, needsRestart, s,
101
- setupPhase, cliEnabled, mcpEnabled,
123
+ setupPhase, cliEnabled, mcpEnabled, skillInstallStatus = 'pending',
102
124
  }: StepReviewProps) {
103
125
  const failedAgents = Object.entries(agentStatuses).filter(([, v]) => v.state === 'error');
104
126
 
@@ -113,9 +135,11 @@ export default function StepReview({
113
135
  // Progress stepper phases — dynamically built based on selected modes
114
136
  type Phase = typeof setupPhase;
115
137
  const showAgentPhase = mcpEnabled && selectedAgents.size > 0;
138
+ const showSkillPhase = selectedAgents.size > 0;
116
139
  const phases: { key: Phase; label: string }[] = [
117
140
  { key: 'saving', label: s.phaseSaving },
118
141
  ...(showAgentPhase ? [{ key: 'agents' as Phase, label: s.phaseAgents }] : []),
142
+ ...(showSkillPhase ? [{ key: 'skills' as Phase, label: s.phaseSkill ?? 'Installing skills' }] : []),
119
143
  { key: 'done', label: s.phaseDone },
120
144
  ];
121
145
  const phaseOrder: Phase[] = phases.map(p => p.key);
@@ -213,6 +237,7 @@ export default function StepReview({
213
237
  selectedAgents={selectedAgents}
214
238
  agentStatuses={agentStatuses}
215
239
  needsRestart={needsRestart}
240
+ skillInstallStatus={skillInstallStatus}
216
241
  s={s}
217
242
  />
218
243
  )}
@@ -222,12 +247,13 @@ export default function StepReview({
222
247
 
223
248
  /* ── Health Check Summary ─────────────────────────────────────────────────── */
224
249
 
225
- function HealthCheckView({ state, selectedAgents, agentStatuses, needsRestart, s }: {
250
+ function HealthCheckView({ state, selectedAgents, agentStatuses, needsRestart, s, skillInstallStatus = 'pending' }: {
226
251
  state: SetupState;
227
252
  selectedAgents: Set<string>;
228
253
  agentStatuses: Record<string, AgentInstallStatus>;
229
254
  needsRestart: boolean;
230
255
  s: SetupMessages;
256
+ skillInstallStatus?: 'pending' | 'installing' | 'ok' | 'error' | 'skipped';
231
257
  }) {
232
258
  const [copied, setCopied] = useState(false);
233
259
 
@@ -251,6 +277,7 @@ function HealthCheckView({ state, selectedAgents, agentStatuses, needsRestart, s
251
277
  const successAgents = Object.values(agentStatuses).filter(a => a.state === 'ok').length;
252
278
  const agentsOk = successAgents > 0;
253
279
  const hasToken = !!state.authToken;
280
+ const skillsOk = skillInstallStatus === 'ok';
254
281
 
255
282
  // Resolve provider display name and model from dynamic config
256
283
  let providerDisplayName = '';
@@ -295,6 +322,19 @@ function HealthCheckView({ state, selectedAgents, agentStatuses, needsRestart, s
295
322
  : (s.healthAgentsNone ?? 'No agents configured'),
296
323
  action: agentsOk ? undefined : (s.healthAgentsAction ?? 'You can add agents later in Settings → Connections.'),
297
324
  },
325
+ ...(selectedAgents.size > 0 ? [{
326
+ ok: skillsOk,
327
+ icon: <Sparkles size={14} />,
328
+ title: s.healthSkills ?? 'Skills',
329
+ detail: skillInstallStatus === 'ok'
330
+ ? (s.healthSkillsOk ?? 'Skills installed successfully')
331
+ : skillInstallStatus === 'error'
332
+ ? (s.healthSkillsError ?? 'Skill installation failed')
333
+ : skillInstallStatus === 'skipped'
334
+ ? (s.healthSkillsSkipped ?? 'Skipped')
335
+ : (s.healthSkillsInstalling ?? 'Installing skills...'),
336
+ action: skillInstallStatus === 'error' ? (s.healthSkillsAction ?? 'You can install skills manually later.') : undefined,
337
+ }] : []),
298
338
  ];
299
339
 
300
340
  return (
@@ -113,6 +113,30 @@ async function installAgents(
113
113
  return updated;
114
114
  }
115
115
 
116
+ /** Phase 2.5: Install skills to selected agents. Returns success status. */
117
+ async function installSkills(
118
+ skillName: string,
119
+ agentKeys: string[],
120
+ ): Promise<boolean> {
121
+ if (agentKeys.length === 0) return true;
122
+
123
+ try {
124
+ const res = await fetch('/api/mcp/install-skill', {
125
+ method: 'POST',
126
+ headers: { 'Content-Type': 'application/json' },
127
+ body: JSON.stringify({
128
+ skill: skillName,
129
+ agents: agentKeys,
130
+ }),
131
+ });
132
+ const data = await res.json();
133
+ return data.ok === true;
134
+ } catch (e) {
135
+ console.warn('[SetupWizard] Skill installation failed:', e);
136
+ return false;
137
+ }
138
+ }
139
+
116
140
  // ─── Component ───────────────────────────────────────────────────────────────
117
141
 
118
142
  export default function SetupWizard() {
@@ -138,6 +162,7 @@ export default function SetupWizard() {
138
162
  const [completed, setCompleted] = useState(false);
139
163
  const [error, setError] = useState('');
140
164
  const [needsRestart, setNeedsRestart] = useState(false);
165
+ const [skillInstallStatus, setSkillInstallStatus] = useState<'pending' | 'installing' | 'ok' | 'error' | 'skipped'>('pending');
141
166
 
142
167
  const [webPortStatus, setWebPortStatus] = useState<PortStatus>({ checking: false, available: null, isSelf: false, suggestion: null });
143
168
  const [mcpPortStatus, setMcpPortStatus] = useState<PortStatus>({ checking: false, available: null, isSelf: false, suggestion: null });
@@ -150,7 +175,7 @@ export default function SetupWizard() {
150
175
  const [agentScope, setAgentScope] = useState<'global' | 'project'>('global');
151
176
  const [agentStatuses, setAgentStatuses] = useState<Record<string, AgentInstallStatus>>({});
152
177
  const [connectionMode, setConnectionMode] = useState<ConnectionMode>({ cli: true, mcp: false });
153
- const [setupPhase, setSetupPhase] = useState<'review' | 'saving' | 'agents' | 'done'>('review');
178
+ const [setupPhase, setSetupPhase] = useState<'review' | 'saving' | 'agents' | 'skills' | 'done'>('review');
154
179
 
155
180
  // Load existing config as defaults on mount, generate token if none exists
156
181
  useEffect(() => {
@@ -357,6 +382,22 @@ export default function SetupWizard() {
357
382
  }
358
383
  }
359
384
 
385
+ // Phase 3: Install skills to selected agents ⭐ NEW
386
+ if (agentKeys.length > 0) {
387
+ setSetupPhase('skills');
388
+ setSkillInstallStatus('installing');
389
+ const skillName = finalState.template === 'zh' ? 'mindos-zh' : 'mindos';
390
+ try {
391
+ const skillOk = await installSkills(skillName, agentKeys);
392
+ setSkillInstallStatus(skillOk ? 'ok' : 'error');
393
+ } catch (e) {
394
+ console.warn('[SetupWizard] skill install failed:', e);
395
+ setSkillInstallStatus('error');
396
+ }
397
+ } else {
398
+ setSkillInstallStatus('skipped');
399
+ }
400
+
360
401
  setSubmitting(false);
361
402
  setCompleted(true);
362
403
  setSetupPhase('done');
@@ -443,6 +484,7 @@ export default function SetupWizard() {
443
484
  setupPhase={setupPhase}
444
485
  cliEnabled={connectionMode.cli}
445
486
  mcpEnabled={connectionMode.mcp}
487
+ skillInstallStatus={skillInstallStatus}
446
488
  />
447
489
  )}
448
490
 
@@ -467,7 +509,7 @@ export default function SetupWizard() {
467
509
  ) : completed ? (
468
510
  // After completing: show Restart button or Go link
469
511
  needsRestart ? (
470
- <RestartButton s={s} newPort={state.webPort} />
512
+ <RestartButton s={s} newPort={state.webPort} webPassword={state.webPassword} />
471
513
  ) : (
472
514
  <a href="/?welcome=1"
473
515
  className="flex items-center gap-1.5 px-5 py-2 text-sm font-medium rounded-lg transition-colors hover:opacity-90 focus-visible:ring-2 focus-visible:ring-ring"
@@ -74,6 +74,12 @@ export function useAcpDetection(): AcpDetectionState {
74
74
  setTrigger((n) => n + 1);
75
75
  }, []);
76
76
 
77
+ useEffect(() => {
78
+ const onSettingsChanged = () => refresh();
79
+ window.addEventListener('mindos:settings-changed', onSettingsChanged);
80
+ return () => window.removeEventListener('mindos:settings-changed', onSettingsChanged);
81
+ }, [refresh]);
82
+
77
83
  useEffect(() => {
78
84
  const isForce = forceRef.current;
79
85
  forceRef.current = false;
@@ -9,19 +9,25 @@ export function useSettingsAiAvailable(): { ready: boolean; loading: boolean } {
9
9
 
10
10
  useEffect(() => {
11
11
  let cancelled = false;
12
- fetch('/api/settings', { cache: 'no-store' })
13
- .then((r) => r.json())
14
- .then((d: SettingsJsonForAi) => {
15
- if (!cancelled) setReady(isAiConfiguredForAsk(d));
16
- })
17
- .catch(() => {
18
- if (!cancelled) setReady(false);
19
- })
20
- .finally(() => {
21
- if (!cancelled) setLoading(false);
22
- });
12
+ const doFetch = () => {
13
+ fetch('/api/settings', { cache: 'no-store' })
14
+ .then((r) => r.json())
15
+ .then((d: SettingsJsonForAi) => {
16
+ if (!cancelled) setReady(isAiConfiguredForAsk(d));
17
+ })
18
+ .catch(() => {
19
+ if (!cancelled) setReady(false);
20
+ })
21
+ .finally(() => {
22
+ if (!cancelled) setLoading(false);
23
+ });
24
+ };
25
+ doFetch();
26
+ const onChanged = () => doFetch();
27
+ window.addEventListener('mindos:settings-changed', onChanged);
23
28
  return () => {
24
29
  cancelled = true;
30
+ window.removeEventListener('mindos:settings-changed', onChanged);
25
31
  };
26
32
  }, []);
27
33
 
@@ -88,6 +88,7 @@ export const onboardingEn = {
88
88
  agentNotInstalled: 'not installed',
89
89
  agentDetected: 'detected',
90
90
  agentNotFound: 'not found',
91
+ agentGetIt: 'Get it →',
91
92
  agentStatusOk: 'configured',
92
93
  agentStatusError: 'failed',
93
94
  agentInstalling: 'Configuring…',
@@ -144,6 +145,12 @@ export const onboardingEn = {
144
145
  healthAgentsPartial: 'Configuration in progress...',
145
146
  healthAgentsNone: 'No agents configured',
146
147
  healthAgentsAction: 'You can add agents later in Settings → Connections.',
148
+ healthSkills: 'Skills',
149
+ healthSkillsOk: 'Skills installed successfully',
150
+ healthSkillsError: 'Skill installation failed',
151
+ healthSkillsSkipped: 'Skipped',
152
+ healthSkillsInstalling: 'Installing skills...',
153
+ healthSkillsAction: 'You can install skills manually later.',
147
154
  healthTokenTitle: 'Auth Token',
148
155
  healthTokenCopy: 'Copy token',
149
156
  healthTokenHint: 'Used by CLI remote mode and MCP connections. Also available in Settings → Connections.',
@@ -303,6 +310,7 @@ export const onboardingZh = {
303
310
  agentNotInstalled: '未安装',
304
311
  agentDetected: '已检测到',
305
312
  agentNotFound: '未找到',
313
+ agentGetIt: '去安装 →',
306
314
  agentStatusOk: '已配置',
307
315
  agentStatusError: '失败',
308
316
  agentInstalling: '配置中…',
@@ -359,6 +367,12 @@ export const onboardingZh = {
359
367
  healthAgentsPartial: '正在配置中...',
360
368
  healthAgentsNone: '未配置 Agent',
361
369
  healthAgentsAction: '可稍后在 设置 → 连接 中添加。',
370
+ healthSkills: 'Skills',
371
+ healthSkillsOk: 'Skill 安装成功',
372
+ healthSkillsError: 'Skill 安装失败',
373
+ healthSkillsSkipped: '已跳过',
374
+ healthSkillsInstalling: '正在安装 Skill…',
375
+ healthSkillsAction: '可稍后手动安装 Skill。',
362
376
  healthTokenTitle: '认证令牌',
363
377
  healthTokenCopy: '复制令牌',
364
378
  healthTokenHint: 'CLI 远程模式和 MCP 连接时使用。也可在 设置 → 连接 中找到。',
@@ -478,7 +478,22 @@ function fileAppendCsv(root, filePath, flags) {
478
478
  const line = escapeCsvRow(values) + '\n';
479
479
 
480
480
  mkdirSync(dirname(full), { recursive: true });
481
- appendFileSync(full, line, 'utf-8');
481
+
482
+ let separator = '';
483
+ if (existsSync(full)) {
484
+ const stat = statSync(full);
485
+ if (stat.size > 0) {
486
+ const fd = openSync(full, 'r');
487
+ try {
488
+ const buf = Buffer.alloc(1);
489
+ readSync(fd, buf, 0, 1, stat.size - 1);
490
+ if (buf[0] !== 0x0a) separator = '\n';
491
+ } finally {
492
+ closeSync(fd);
493
+ }
494
+ }
495
+ }
496
+ appendFileSync(full, separator + line, 'utf-8');
482
497
 
483
498
  const content = readFileSync(full, 'utf-8');
484
499
  const newRowCount = content.trim().split('\n').length;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geminilight/mindos",
3
- "version": "0.6.47",
3
+ "version": "0.6.49",
4
4
  "description": "MindOS — Human-Agent Collaborative Mind System. Local-first knowledge base that syncs your mind to all AI Agents via MCP.",
5
5
  "keywords": [
6
6
  "mindos",
@@ -39,6 +39,8 @@ cp -r app/.next/static "$WORK/app/.next/static"
39
39
  # Copy public assets
40
40
  mkdir -p "$WORK/app"
41
41
  cp -r app/public "$WORK/app/public"
42
+ # Copy skill definitions (not included in standalone output, needed at runtime)
43
+ [ -d app/data/skills ] && cp -r app/data/skills "$WORK/app/data/skills"
42
44
 
43
45
  # ── MCP server ──
44
46
  echo " Copying MCP..."
@@ -69,6 +71,7 @@ for f in \
69
71
  "app/.next/standalone/node_modules" \
70
72
  "app/.next/standalone/.next/server" \
71
73
  "app/.next/static" \
74
+ "app/data/skills" \
72
75
  "mcp/dist/index.cjs" \
73
76
  "package.json"; do
74
77
  if [ ! -e "$VERIFY/$f" ]; then