@geminilight/mindos 0.6.40 → 0.6.41

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 (287) 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 +18 -18
  4. package/_standalone/.next/build-manifest.json +3 -3
  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/react-loadable-manifest.json +5 -1
  10. package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
  11. package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/_standalone/.next/server/app/_global-error.html +2 -2
  13. package/_standalone/.next/server/app/_global-error.rsc +1 -1
  14. package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  17. package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/_standalone/.next/server/app/_not-found/page.js +1 -1
  21. package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
  24. package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
  25. package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
  26. package/_standalone/.next/server/app/agents/page.js +2 -2
  27. package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
  28. package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  29. package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
  30. package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
  31. package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
  32. package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
  33. package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
  34. package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
  35. package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
  36. package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
  37. package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
  38. package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
  39. package/_standalone/.next/server/app/api/ask/route.js +1 -1
  40. package/_standalone/.next/server/app/api/ask/route.js.nft.json +1 -1
  41. package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
  42. package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
  43. package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
  44. package/_standalone/.next/server/app/api/backlinks/route.js.nft.json +1 -1
  45. package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
  46. package/_standalone/.next/server/app/api/bootstrap/route.js.nft.json +1 -1
  47. package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
  48. package/_standalone/.next/server/app/api/changes/route.js.nft.json +1 -1
  49. package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
  50. package/_standalone/.next/server/app/api/export/route.js.nft.json +1 -1
  51. package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
  52. package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
  53. package/_standalone/.next/server/app/api/file/import/route.js.nft.json +1 -1
  54. package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
  55. package/_standalone/.next/server/app/api/file/route.js.nft.json +1 -1
  56. package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
  57. package/_standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  58. package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  59. package/_standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  60. package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  61. package/_standalone/.next/server/app/api/graph/route.js.nft.json +1 -1
  62. package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
  63. package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  64. package/_standalone/.next/server/app/api/inbox/route.js.nft.json +1 -1
  65. package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
  66. package/_standalone/.next/server/app/api/init/route.js.nft.json +1 -1
  67. package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  68. package/_standalone/.next/server/app/api/mcp/agents/route.js.nft.json +1 -1
  69. package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
  70. package/_standalone/.next/server/app/api/mcp/install/route.js +1 -1
  71. package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
  72. package/_standalone/.next/server/app/api/mcp/install-skill/route.js.nft.json +1 -1
  73. package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
  74. package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
  75. package/_standalone/.next/server/app/api/mcp/status/route.js +1 -1
  76. package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
  77. package/_standalone/.next/server/app/api/monitoring/route.js.nft.json +1 -1
  78. package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
  79. package/_standalone/.next/server/app/api/recent-files/route.js.nft.json +1 -1
  80. package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
  81. package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  82. package/_standalone/.next/server/app/api/search/route.js.nft.json +1 -1
  83. package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  84. package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
  85. package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
  86. package/_standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
  87. package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
  88. package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
  89. package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
  90. package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
  91. package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
  92. package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
  93. package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
  94. package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  95. package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
  96. package/_standalone/.next/server/app/api/tree-version/route.js.nft.json +1 -1
  97. package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
  98. package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
  99. package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  100. package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
  101. package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
  102. package/_standalone/.next/server/app/api/workflows/route.js.nft.json +1 -1
  103. package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
  104. package/_standalone/.next/server/app/changes/page.js +1 -1
  105. package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
  106. package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
  107. package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
  108. package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
  109. package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
  110. package/_standalone/.next/server/app/echo/page.js +1 -1
  111. package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
  112. package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
  113. package/_standalone/.next/server/app/explore/page.js +1 -1
  114. package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
  115. package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
  116. package/_standalone/.next/server/app/help/page.js +2 -2
  117. package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
  118. package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
  119. package/_standalone/.next/server/app/login/page.js +1 -1
  120. package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
  121. package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  122. package/_standalone/.next/server/app/page.js +2 -2
  123. package/_standalone/.next/server/app/page.js.nft.json +1 -1
  124. package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  125. package/_standalone/.next/server/app/setup/page.js +2 -2
  126. package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
  127. package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  128. package/_standalone/.next/server/app/trash/page.js +3 -3
  129. package/_standalone/.next/server/app/trash/page.js.nft.json +1 -1
  130. package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
  131. package/_standalone/.next/server/app/view/[...path]/page.js +3 -3
  132. package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
  133. package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
  134. package/_standalone/.next/server/app-paths-manifest.json +18 -18
  135. package/_standalone/.next/server/chunks/1550.js +1 -1
  136. package/_standalone/.next/server/chunks/2190.js +11 -0
  137. package/_standalone/.next/server/chunks/{6365.js → 2536.js} +2 -2
  138. package/_standalone/.next/server/chunks/5648.js +2 -0
  139. package/_standalone/.next/server/chunks/8388.js +1 -1
  140. package/_standalone/.next/server/chunks/953.js +1 -1
  141. package/_standalone/.next/server/chunks/9539.js +219 -0
  142. package/_standalone/.next/server/middleware-build-manifest.js +1 -1
  143. package/_standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  144. package/_standalone/.next/server/next-font-manifest.js +1 -1
  145. package/_standalone/.next/server/next-font-manifest.json +1 -1
  146. package/_standalone/.next/server/pages/500.html +2 -2
  147. package/_standalone/.next/server/server-reference-manifest.js +1 -1
  148. package/_standalone/.next/server/server-reference-manifest.json +1 -1
  149. package/_standalone/.next/static/chunks/1053-b70535785cc5aaee.js +29 -0
  150. package/_standalone/.next/static/chunks/{8663-de911d2d395622be.js → 1880-c2a9e76201841c86.js} +1 -1
  151. package/_standalone/.next/static/chunks/3637.0541ac2d0ea7de1f.js +1 -0
  152. package/_standalone/.next/static/chunks/4563-b2a2ce80aff845af.js +6 -0
  153. package/_standalone/.next/static/chunks/6981-3d7dcac2d12a5670.js +1 -0
  154. package/_standalone/.next/static/chunks/7144-5febf62f1a79fe64.js +1 -0
  155. package/_standalone/.next/static/chunks/app/agents/[agentKey]/page-773071a99c4daac2.js +1 -0
  156. package/_standalone/.next/static/chunks/app/agents/page-6102a884b2cb3cfe.js +5 -0
  157. package/_standalone/.next/static/chunks/app/help/page-2325d25b6846ca07.js +1 -0
  158. package/_standalone/.next/static/chunks/app/{layout-9378c1c8d3e5761b.js → layout-42cdbce19f404567.js} +34 -34
  159. package/_standalone/.next/static/chunks/app/{page-9bae420fbbdc5fff.js → page-8c9643b649e01735.js} +1 -1
  160. package/_standalone/.next/static/chunks/app/setup/page-d158b8cb533feb1e.js +1 -0
  161. package/_standalone/.next/static/chunks/app/trash/{page-b61ef2d5cd4f8d73.js → page-e9ab74ffeb96af41.js} +1 -1
  162. package/_standalone/.next/static/chunks/app/view/[...path]/page-764a69a1c8bd4eef.js +12 -0
  163. package/_standalone/.next/static/chunks/{webpack-c28c55d0a6021a6b.js → webpack-7b276daaa930d480.js} +1 -1
  164. package/_standalone/.next/static/css/bc9179074eaf65ae.css +1 -0
  165. package/_standalone/.next/trace +63 -63
  166. package/_standalone/__tests__/api/mcp-install.test.ts +23 -0
  167. package/_standalone/__tests__/cli/agent-routing.test.ts +232 -0
  168. package/_standalone/__tests__/cli/file-subcommands.test.ts +379 -0
  169. package/_standalone/__tests__/core/tools.test.ts +3 -6
  170. package/_standalone/components/FileTree.tsx +3 -2
  171. package/_standalone/components/MarkdownView.tsx +30 -15
  172. package/_standalone/components/RightAskPanel.tsx +36 -6
  173. package/_standalone/components/Sidebar.tsx +3 -3
  174. package/_standalone/components/agents/AgentsMcpSection.tsx +3 -0
  175. package/_standalone/components/settings/McpAgentInstall.tsx +94 -27
  176. package/_standalone/components/settings/McpSkillsSection.tsx +1 -1
  177. package/_standalone/components/settings/McpTab.tsx +484 -340
  178. package/_standalone/components/settings/SettingsContent.tsx +12 -6
  179. package/_standalone/components/settings/types.ts +3 -0
  180. package/_standalone/components/setup/StepAgents.tsx +113 -47
  181. package/_standalone/components/setup/StepReview.tsx +14 -27
  182. package/_standalone/components/setup/types.ts +6 -0
  183. package/_standalone/data/skills/mindos/SKILL.md +92 -92
  184. package/_standalone/data/skills/mindos/references/write-supplement.md +119 -0
  185. package/_standalone/data/skills/mindos-zh/SKILL.md +100 -104
  186. package/_standalone/data/skills/mindos-zh/references/write-supplement.md +119 -0
  187. package/_standalone/lib/i18n/modules/features.ts +4 -4
  188. package/_standalone/lib/i18n/modules/knowledge.ts +4 -0
  189. package/_standalone/lib/i18n/modules/onboarding.ts +40 -30
  190. package/_standalone/lib/i18n/modules/settings.ts +78 -6
  191. package/_standalone/lib/mcp-snippets.ts +5 -1
  192. package/_standalone/tsconfig.tsbuildinfo +1 -1
  193. package/app/app/api/ask/route.ts +3 -2
  194. package/app/app/api/mcp/install/route.ts +2 -1
  195. package/app/app/api/mcp/status/route.ts +14 -6
  196. package/app/app/view/[...path]/ViewPageClient.tsx +12 -27
  197. package/app/components/FileTree.tsx +3 -2
  198. package/app/components/MarkdownView.tsx +30 -15
  199. package/app/components/RightAskPanel.tsx +36 -6
  200. package/app/components/Sidebar.tsx +3 -3
  201. package/app/components/agents/AgentsMcpSection.tsx +3 -0
  202. package/app/components/help/HelpContent.tsx +1 -0
  203. package/app/components/settings/McpAgentInstall.tsx +94 -27
  204. package/app/components/settings/McpSkillsSection.tsx +1 -1
  205. package/app/components/settings/McpTab.tsx +484 -340
  206. package/app/components/settings/SettingsContent.tsx +12 -6
  207. package/app/components/settings/types.ts +3 -0
  208. package/app/components/setup/StepAgents.tsx +113 -47
  209. package/app/components/setup/StepReview.tsx +14 -27
  210. package/app/components/setup/index.tsx +12 -11
  211. package/app/components/setup/types.ts +6 -0
  212. package/app/data/skills/mindos/SKILL.md +92 -92
  213. package/app/data/skills/mindos/references/write-supplement.md +119 -0
  214. package/app/data/skills/mindos-zh/SKILL.md +100 -104
  215. package/app/data/skills/mindos-zh/references/write-supplement.md +119 -0
  216. package/app/lib/fs.ts +0 -6
  217. package/app/lib/i18n/modules/features.ts +4 -4
  218. package/app/lib/i18n/modules/knowledge.ts +4 -0
  219. package/app/lib/i18n/modules/onboarding.ts +40 -30
  220. package/app/lib/i18n/modules/settings.ts +78 -6
  221. package/app/lib/mcp-agents.ts +1 -2
  222. package/app/lib/mcp-snippets.ts +5 -1
  223. package/app/lib/renderers/index.ts +2 -1
  224. package/bin/cli.js +168 -1404
  225. package/bin/commands/agent.js +156 -20
  226. package/bin/commands/api.js +14 -11
  227. package/bin/commands/ask.js +79 -68
  228. package/bin/commands/build.js +26 -0
  229. package/bin/commands/config.js +170 -0
  230. package/bin/commands/dev.js +58 -0
  231. package/bin/commands/doctor.js +205 -0
  232. package/bin/commands/file.js +551 -36
  233. package/bin/commands/gateway.js +42 -0
  234. package/bin/commands/init-skills.js +56 -0
  235. package/bin/commands/logs.js +32 -0
  236. package/bin/commands/mcp-cmd.js +57 -0
  237. package/bin/commands/onboard.js +25 -0
  238. package/bin/commands/open.js +41 -0
  239. package/bin/commands/restart.js +48 -0
  240. package/bin/commands/search.js +16 -14
  241. package/bin/commands/space.js +96 -25
  242. package/bin/commands/start.js +262 -0
  243. package/bin/commands/status.js +2 -2
  244. package/bin/commands/stop.js +14 -0
  245. package/bin/commands/sync-cmd.js +134 -0
  246. package/bin/commands/token.js +98 -0
  247. package/bin/commands/uninstall.js +154 -0
  248. package/bin/commands/update.js +286 -0
  249. package/bin/lib/build.js +1 -1
  250. package/bin/lib/colors.js +8 -7
  251. package/bin/lib/command.js +37 -96
  252. package/bin/lib/config.js +5 -0
  253. package/bin/lib/csv.js +19 -0
  254. package/bin/lib/jsonc.js +12 -0
  255. package/bin/lib/markdown.js +69 -0
  256. package/bin/lib/mcp-agents.js +1 -6
  257. package/bin/lib/mcp-build.js +1 -1
  258. package/bin/lib/mcp-install.js +2 -1
  259. package/bin/lib/one-shot.js +88 -0
  260. package/bin/lib/path-expand.js +9 -0
  261. package/bin/lib/remote.js +65 -0
  262. package/bin/lib/repl.js +167 -0
  263. package/bin/lib/{utils.js → shell.js} +10 -26
  264. package/bin/lib/skill-check.js +1 -1
  265. package/bin/lib/sse-stream.js +167 -0
  266. package/package.json +2 -2
  267. package/scripts/setup.js +182 -120
  268. package/skills/mindos/SKILL.md +92 -92
  269. package/skills/mindos-zh/SKILL.md +100 -104
  270. package/_standalone/.next/server/chunks/1955.js +0 -11
  271. package/_standalone/.next/server/chunks/3680.js +0 -1
  272. package/_standalone/.next/server/chunks/4497.js +0 -219
  273. package/_standalone/.next/server/chunks/5560.js +0 -2
  274. package/_standalone/.next/static/chunks/1053-0adaccc98a752a58.js +0 -29
  275. package/_standalone/.next/static/chunks/3637.f9a42cca59fd5bb5.js +0 -1
  276. package/_standalone/.next/static/chunks/4563-c2afaeacb241d1d0.js +0 -6
  277. package/_standalone/.next/static/chunks/6090-c98268ca726a68d3.js +0 -1
  278. package/_standalone/.next/static/chunks/9371-575600301da5d6bb.js +0 -1
  279. package/_standalone/.next/static/chunks/app/agents/[agentKey]/page-3e08abb495ecd5fd.js +0 -1
  280. package/_standalone/.next/static/chunks/app/agents/page-e7e0f87ad3d765ac.js +0 -5
  281. package/_standalone/.next/static/chunks/app/help/page-3d0e1ceaa4abc243.js +0 -1
  282. package/_standalone/.next/static/chunks/app/setup/page-99ed3d1bb6b8f4ef.js +0 -1
  283. package/_standalone/.next/static/chunks/app/view/[...path]/page-44fa78cbea613a78.js +0 -12
  284. package/_standalone/.next/static/css/d300701f384db50d.css +0 -1
  285. package/_standalone/components/renderers/agent-inspector/manifest.ts +0 -16
  286. /package/_standalone/.next/static/{rZLs1krFuduixvcVNe6q3 → Ij3PFh-a0zi5K_ANoSAW0}/_buildManifest.js +0 -0
  287. /package/_standalone/.next/static/{rZLs1krFuduixvcVNe6q3 → Ij3PFh-a0zi5K_ANoSAW0}/_ssgManifest.js +0 -0
@@ -13,6 +13,7 @@ import {
13
13
  type ToolDefinition,
14
14
  SessionManager,
15
15
  SettingsManager,
16
+ bashTool,
16
17
  } from '@mariozechner/pi-coding-agent';
17
18
  import { NextRequest, NextResponse } from 'next/server';
18
19
  import fs from 'fs';
@@ -316,7 +317,7 @@ function toPiCustomToolDefinitions(tools: AgentTool<any>[]): ToolDefinition<any,
316
317
  params: args,
317
318
  result: outputText.startsWith('Error:') ? 'error' : 'ok',
318
319
  message: outputText.slice(0, 200),
319
- agentName: 'MindOS Ask',
320
+ agentName: 'MindOS',
320
321
  });
321
322
  } catch {
322
323
  // logging must never kill the stream
@@ -669,7 +670,7 @@ export async function POST(req: NextRequest) {
669
670
  resourceLoader,
670
671
  sessionManager: SessionManager.inMemory(),
671
672
  settingsManager,
672
- tools: [],
673
+ tools: askMode === 'agent' ? [bashTool] : [],
673
674
  customTools,
674
675
  });
675
676
 
@@ -5,10 +5,11 @@ import path from 'path';
5
5
  import { MCP_AGENTS, expandHome } from '@/lib/mcp-agents';
6
6
  import { readSettings } from '@/lib/settings';
7
7
 
8
- /** Parse JSONC — strips single-line (//) and block comments before JSON.parse */
8
+ /** Parse JSONC — strips comments before JSON.parse. Returns {} for empty/whitespace-only input. */
9
9
  function parseJsonc(text: string): Record<string, unknown> {
10
10
  let stripped = text.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*$)/gm, (m, g) => g ? '' : m);
11
11
  stripped = stripped.replace(/\/\*[\s\S]*?\*\//g, '');
12
+ if (!stripped.trim()) return {};
12
13
  return JSON.parse(stripped);
13
14
  }
14
15
 
@@ -2,18 +2,29 @@ export const dynamic = 'force-dynamic';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
3
  import { readSettings } from '@/lib/settings';
4
4
  import { maskToken } from '@/lib/format';
5
+ import { networkInterfaces } from 'os';
5
6
 
6
7
  /** Parse hostname from Host header, handling IPv6 brackets */
7
8
  function parseHostname(host: string): string {
8
- // IPv6: [::1]:3003 → [::1]
9
9
  if (host.includes(']')) {
10
10
  return host.slice(0, host.lastIndexOf(']') + 1);
11
11
  }
12
- // IPv4/hostname: 192.168.1.1:3003 → 192.168.1.1
13
12
  const colonIdx = host.lastIndexOf(':');
14
13
  return colonIdx > 0 ? host.slice(0, colonIdx) : host;
15
14
  }
16
15
 
16
+ /** Get first non-internal IPv4 address */
17
+ function getLocalIP(): string | null {
18
+ try {
19
+ for (const ifaces of Object.values(networkInterfaces())) {
20
+ for (const iface of ifaces ?? []) {
21
+ if (iface.family === 'IPv4' && !iface.internal) return iface.address;
22
+ }
23
+ }
24
+ } catch { /* ignore */ }
25
+ return null;
26
+ }
27
+
17
28
  export async function GET(req: NextRequest) {
18
29
  try {
19
30
  const settings = readSettings();
@@ -52,12 +63,9 @@ export async function GET(req: NextRequest) {
52
63
  port,
53
64
  toolCount: running ? 24 : 0,
54
65
  authConfigured,
55
- // Masked for display; full token only used server-side in snippet generation
56
66
  maskedToken: authConfigured ? maskToken(token) : undefined,
57
- // Full token for config snippet copy — this API is protected by proxy.ts middleware
58
- // (same-origin or bearer token required). Consistent with /api/settings which also
59
- // exposes the token to authenticated users.
60
67
  authToken: authConfigured ? token : undefined,
68
+ localIP: getLocalIP(),
61
69
  });
62
70
  } catch (err) {
63
71
  return NextResponse.json({ error: String(err) }, { status: 500 });
@@ -58,8 +58,8 @@ export default function ViewPageClient({
58
58
  );
59
59
 
60
60
  const [useRaw, setUseRaw] = useRendererState<boolean>('_raw', filePath, false);
61
- // Global graph mode — shared across all md files (not per-file)
62
- const [graphMode, setGraphMode] = useRendererState<boolean>('_graphMode', '_global', false);
61
+ // Graph mode — per-view, resets when navigating to a different file
62
+ const [graphMode, setGraphMode] = useState(false);
63
63
  const router = useRouter();
64
64
  const [editing, setEditing] = useState(initialEditing || content === '');
65
65
  const [editContent, setEditContent] = useState(content);
@@ -167,7 +167,7 @@ export default function ViewPageClient({
167
167
  setGraphMode(prev => !prev);
168
168
  }, [setGraphMode]);
169
169
 
170
- const effectiveGraphMode = hydrated ? graphMode : false;
170
+ const effectiveGraphMode = graphMode;
171
171
 
172
172
  // Resolve renderer: for md files, graph mode overrides normal resolution
173
173
  const registryRenderer = resolveRenderer(filePath, extension);
@@ -383,22 +383,6 @@ export default function ViewPageClient({
383
383
  <span className="text-xs text-error hidden sm:inline">{saveError}</span>
384
384
  )}
385
385
 
386
- {/* Graph toggle — only for md files, hidden when graph plugin is disabled */}
387
- {extension === 'md' && !editing && !isDraft && isRendererEnabled('graph') && (
388
- <button
389
- onClick={handleToggleGraph}
390
- className="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors font-display"
391
- style={{
392
- background: effectiveGraphMode ? `${'var(--amber)'}22` : 'var(--muted)',
393
- color: effectiveGraphMode ? 'var(--amber)' : 'var(--muted-foreground)',
394
- }}
395
- title={effectiveGraphMode ? 'Switch to document view' : 'Switch to Wiki Graph'}
396
- >
397
- {effectiveGraphMode ? <FileText size={13} /> : <Share2 size={13} />}
398
- <span className="hidden sm:inline">{effectiveGraphMode ? 'Doc' : 'Graph'}</span>
399
- </button>
400
- )}
401
-
402
386
  {/* Renderer toggle — only shown when a custom renderer exists (excludes graph-mode override) */}
403
387
  {registryRenderer && !editing && !isDraft && !graphRenderer && (
404
388
  <button
@@ -463,14 +447,6 @@ export default function ViewPageClient({
463
447
  >
464
448
  <Star size={16} className={pinned ? 'fill-[var(--amber)] text-[var(--amber)]' : ''} />
465
449
  </button>
466
- <button
467
- type="button"
468
- onClick={() => setExportOpen(true)}
469
- className="p-1.5 rounded-md text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
470
- title={t.fileTree.export}
471
- >
472
- <Download size={16} />
473
- </button>
474
450
  <button
475
451
  ref={moreRef}
476
452
  type="button"
@@ -485,6 +461,15 @@ export default function ViewPageClient({
485
461
  ref={moreMenuRef}
486
462
  className="absolute right-0 top-full mt-1 z-50 min-w-[160px] rounded-lg border border-border bg-card shadow-lg py-1"
487
463
  >
464
+ {extension === 'md' && !editing && !isDraft && isRendererEnabled('graph') && (
465
+ <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-foreground hover:bg-muted transition-colors text-left" onClick={() => { setMoreOpen(false); handleToggleGraph(); }}>
466
+ {effectiveGraphMode ? <FileText size={14} className="shrink-0" /> : <Share2 size={14} className="shrink-0" />}
467
+ {effectiveGraphMode ? (t.view?.switchToDoc ?? 'Document view') : (t.view?.switchToGraph ?? 'Wiki Graph')}
468
+ </button>
469
+ )}
470
+ <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-foreground hover:bg-muted transition-colors text-left" onClick={() => { setMoreOpen(false); setExportOpen(true); }}>
471
+ <Download size={14} className="shrink-0" /> {t.fileTree?.export ?? 'Export'}
472
+ </button>
488
473
  <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-foreground hover:bg-muted transition-colors text-left" onClick={handleCopyPath}>
489
474
  <Copy size={14} className="shrink-0" /> {t.view?.copyPath ?? t.fileTree?.copyPath ?? 'Copy Path'}
490
475
  </button>
@@ -77,11 +77,12 @@ function countContentFiles(node: FileNode): number {
77
77
  return (node.children ?? []).reduce((sum, c) => sum + countContentFiles(c), 0);
78
78
  }
79
79
 
80
- /** Filter out system files and dot-entries that shouldn't appear by default. */
80
+ /** Filter out hidden entries (dot-files at root, system files) when show-hidden is off. */
81
81
  function filterHiddenNodes(nodes: FileNode[], isRoot: boolean): FileNode[] {
82
82
  return nodes.filter(node => {
83
83
  if (isRoot && node.name.startsWith('.')) return false;
84
84
  if (node.type === 'file' && SYSTEM_FILES.has(node.name)) return false;
85
+ if (node.type === 'directory' && node.name.startsWith('.')) return false;
85
86
  return true;
86
87
  });
87
88
  }
@@ -426,7 +427,7 @@ function DirectoryNode({ node, depth, currentPath, onNavigate, maxOpenDepth, onI
426
427
  onClick={toggle}
427
428
  className="shrink-0 p-1 rounded hover:bg-muted text-muted-foreground transition-colors"
428
429
  style={{ marginLeft: `${depth * 12 + 4}px` }}
429
- aria-label={open ? 'Collapse' : 'Expand'}
430
+ aria-label={open ? `Collapse ${node.name}` : `Expand ${node.name}`}
430
431
  >
431
432
  <span className="block transition-transform duration-150" style={{ transform: open ? 'rotate(0deg)' : 'rotate(-90deg)' }}>
432
433
  <ChevronDown size={13} />
@@ -51,11 +51,18 @@ function CopyButton({ code }: { code: string }) {
51
51
  );
52
52
  }
53
53
 
54
- // Heading components with suppressHydrationWarning to prevent
55
- // rehype-slug + emoji hydration mismatches between server and client
54
+ // react-markdown passes an AST `node` prop to custom components;
55
+ // strip it (and any other non-DOM keys) before forwarding to the real element.
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ function stripNonDom(props: Record<string, any>): Record<string, any> {
58
+ const { node, inline, ordered, depth, isHeader, ...domProps } = props;
59
+ return domProps;
60
+ }
61
+
56
62
  function makeHeading(Tag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6') {
57
- const HeadingComponent = ({ children, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
58
- <Tag {...props} suppressHydrationWarning>{children}</Tag>
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ const HeadingComponent = ({ children, ...props }: any) => (
65
+ <Tag {...stripNonDom(props)} suppressHydrationWarning>{children}</Tag>
59
66
  );
60
67
  HeadingComponent.displayName = Tag;
61
68
  return HeadingComponent;
@@ -68,11 +75,12 @@ const components: Components = {
68
75
  h4: makeHeading('h4'),
69
76
  h5: makeHeading('h5'),
70
77
  h6: makeHeading('h6'),
71
- code({ children, ...props }) {
72
- return <code {...props} suppressHydrationWarning>{children}</code>;
78
+ code({ children, node, ...rest }) {
79
+ void node;
80
+ return <code {...stripNonDom(rest)} suppressHydrationWarning>{children}</code>;
73
81
  },
74
- pre({ children, ...props }) {
75
- // Extract code string from children
82
+ pre({ children, node, ...rest }) {
83
+ void node;
76
84
  let codeString = '';
77
85
  if (children && typeof children === 'object' && 'props' in children) {
78
86
  const codeEl = children as React.ReactElement<{ children?: React.ReactNode }>;
@@ -80,31 +88,38 @@ const components: Components = {
80
88
  }
81
89
  return (
82
90
  <div className="relative group">
83
- <pre {...props} suppressHydrationWarning>{children}</pre>
91
+ <pre {...stripNonDom(rest)} suppressHydrationWarning>{children}</pre>
84
92
  <CopyButton code={codeString} />
85
93
  </div>
86
94
  );
87
95
  },
88
- li({ children, ...props }) {
89
- return <li {...props} suppressHydrationWarning>{children}</li>;
96
+ li({ children, node, ...rest }) {
97
+ void node;
98
+ return <li {...stripNonDom(rest)} suppressHydrationWarning>{children}</li>;
99
+ },
100
+ p({ children, node, ...rest }) {
101
+ void node;
102
+ return <p {...stripNonDom(rest)} suppressHydrationWarning>{children}</p>;
90
103
  },
91
- a({ href, children, ...props }) {
104
+ a({ href, children, node, ...rest }) {
105
+ void node;
92
106
  const isExternal = href?.startsWith('http');
93
107
  return (
94
108
  <a
95
109
  href={href}
96
110
  target={isExternal ? '_blank' : undefined}
97
111
  rel={isExternal ? 'noopener noreferrer' : undefined}
98
- {...props}
112
+ {...stripNonDom(rest)}
99
113
  >
100
114
  {children}
101
115
  </a>
102
116
  );
103
117
  },
104
- img({ src, alt, ...props }) {
118
+ img({ src, alt, node, ...rest }) {
119
+ void node;
105
120
  if (!src) return null;
106
121
  // eslint-disable-next-line @next/next/no-img-element
107
- return <img src={src} alt={alt ?? ''} {...props} />;
122
+ return <img src={src} alt={alt ?? ''} {...stripNonDom(rest)} />;
108
123
  },
109
124
  };
110
125
 
@@ -9,7 +9,9 @@ import { useResizeDrag } from '@/hooks/useResizeDrag';
9
9
  const DEFAULT_WIDTH = 420;
10
10
  const MIN_WIDTH = 400;
11
11
  const MAX_WIDTH_ABS = 4000;
12
- const FOCUS_SNAP_THRESHOLD = 80;
12
+ const ENTER_SNAP_THRESHOLD = 80;
13
+ const EXIT_SNAP_THRESHOLD = 16;
14
+ const MIN_CONTENT_WIDTH = 360;
13
15
 
14
16
  import type { AcpAgentSelection } from '@/hooks/useAskModal';
15
17
 
@@ -37,6 +39,7 @@ export default function RightAskPanel({
37
39
  maximized = false, onMaximize, sidebarOffset = 0,
38
40
  }: RightAskPanelProps) {
39
41
  const snapFiredRef = useRef(false);
42
+ const justExitedMaxRef = useRef(false);
40
43
 
41
44
  const maxAvailable = typeof window !== 'undefined'
42
45
  ? window.innerWidth - sidebarOffset
@@ -45,12 +48,28 @@ export default function RightAskPanel({
45
48
  const handleResize = useCallback((w: number) => {
46
49
  if (snapFiredRef.current) return;
47
50
  const clamped = Math.min(w, maxAvailable);
48
- if (maximized && clamped < maxAvailable - FOCUS_SNAP_THRESHOLD && onMaximize) {
51
+
52
+ // Exit maximized: user drags right even a little (16px) → exit immediately
53
+ if (maximized && clamped < maxAvailable - EXIT_SNAP_THRESHOLD && onMaximize) {
54
+ justExitedMaxRef.current = true;
49
55
  onMaximize();
50
- onWidthChange(clamped);
56
+ const maxPanelForContent = typeof window !== 'undefined'
57
+ ? window.innerWidth - sidebarOffset - MIN_CONTENT_WIDTH
58
+ : clamped;
59
+ onWidthChange(Math.min(clamped, maxPanelForContent));
51
60
  return;
52
61
  }
53
- if (!maximized && clamped >= maxAvailable - FOCUS_SNAP_THRESHOLD && onMaximize) {
62
+
63
+ // Snap to fullscreen: panel near max edge OR content squeezed below minimum.
64
+ // Suppress content-based snap while justExitedMaxRef is true (user recently
65
+ // exited fullscreen and panel is still wide); only re-enable once the panel
66
+ // has been shrunk enough that content is comfortable (reset in handleMouseDown).
67
+ const contentRemaining = typeof window !== 'undefined'
68
+ ? window.innerWidth - sidebarOffset - clamped
69
+ : Infinity;
70
+ const shouldSnap = clamped >= maxAvailable - ENTER_SNAP_THRESHOLD
71
+ || (!justExitedMaxRef.current && contentRemaining < MIN_CONTENT_WIDTH);
72
+ if (!maximized && shouldSnap && onMaximize) {
54
73
  snapFiredRef.current = true;
55
74
  onMaximize();
56
75
  return;
@@ -58,7 +77,7 @@ export default function RightAskPanel({
58
77
  if (!maximized) {
59
78
  onWidthChange(clamped);
60
79
  }
61
- }, [maxAvailable, onMaximize, maximized, onWidthChange]);
80
+ }, [maxAvailable, sidebarOffset, onMaximize, maximized, onWidthChange]);
62
81
 
63
82
  const handleResizeEnd = useCallback((w: number) => {
64
83
  if (snapFiredRef.current) return;
@@ -77,8 +96,19 @@ export default function RightAskPanel({
77
96
 
78
97
  const handleMouseDown = useCallback((e: React.MouseEvent) => {
79
98
  snapFiredRef.current = false;
99
+ // Only re-enable content-based snap once the panel has been shrunk enough
100
+ // that content is comfortably above minimum. This prevents the bounce:
101
+ // exit fullscreen → new drag → immediately re-snap because panel still wide.
102
+ if (justExitedMaxRef.current) {
103
+ const currentContent = typeof window !== 'undefined'
104
+ ? window.innerWidth - sidebarOffset - width
105
+ : Infinity;
106
+ if (currentContent >= MIN_CONTENT_WIDTH) {
107
+ justExitedMaxRef.current = false;
108
+ }
109
+ }
80
110
  rawMouseDown(e);
81
- }, [rawMouseDown]);
111
+ }, [rawMouseDown, sidebarOffset, width]);
82
112
 
83
113
  const effectiveWidth = maximized
84
114
  ? `calc(100vw - ${sidebarOffset}px)`
@@ -102,13 +102,13 @@ export default function Sidebar({ fileTree, collapsed = false, onCollapse, onExp
102
102
  </button>
103
103
  {/* Desktop action buttons — trimmed to 4 */}
104
104
  <div className="hidden md:flex items-center gap-1">
105
- <button onClick={() => setSearchOpen(true)} className="p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title={t.sidebar.searchTitle} aria-label={t.sidebar.searchTitle}>
105
+ <button onClick={() => setSearchOpen(true)} className="p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title={t.sidebar.searchTitle} aria-hidden="true" tabIndex={-1}>
106
106
  <Search size={15} />
107
107
  </button>
108
- <button onClick={() => setSettingsOpen(true)} className="p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title={t.sidebar.settingsTitle} aria-label={t.sidebar.settingsTitle}>
108
+ <button onClick={() => setSettingsOpen(true)} className="p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title={t.sidebar.settingsTitle} aria-hidden="true" tabIndex={-1}>
109
109
  <Settings size={15} />
110
110
  </button>
111
- <button onClick={onCollapse} className="p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title={t.sidebar.collapseTitle} aria-label={t.sidebar.collapseTitle}>
111
+ <button onClick={onCollapse} className="p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors" title={t.sidebar.collapseTitle} aria-hidden="true" tabIndex={-1}>
112
112
  <PanelLeftClose size={15} />
113
113
  </button>
114
114
  </div>
@@ -339,6 +339,9 @@ function ByAgentView({
339
339
  variant="primary"
340
340
  />
341
341
  )}
342
+ {!agent.installed && (
343
+ <span className="text-2xs text-muted-foreground/60">or CLI Skill</span>
344
+ )}
342
345
  </div>
343
346
  </div>
344
347
 
@@ -18,6 +18,7 @@ function Section({ id, icon, title, defaultOpen = false, children }: {
18
18
 
19
19
  return (
20
20
  <div id={id} className="bg-card border border-border rounded-lg overflow-hidden scroll-mt-4">
21
+ <h2 className="sr-only">{title}</h2>
21
22
  <button
22
23
  onClick={() => setOpen(v => !v)}
23
24
  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"
@@ -1,14 +1,16 @@
1
1
  'use client';
2
2
 
3
3
  import { useState } from 'react';
4
- import { CheckCircle2, AlertCircle, Loader2 } from 'lucide-react';
4
+ import { CheckCircle2, AlertCircle, Loader2, Copy } from 'lucide-react';
5
5
  import CustomSelect from '@/components/CustomSelect';
6
6
  import { apiFetch } from '@/lib/api';
7
+ import { copyToClipboard } from '@/lib/clipboard';
8
+ import { toast } from '@/lib/toast';
7
9
  import type { AgentInfo, McpAgentInstallProps } from './types';
8
10
 
9
11
  /* ── Agent Install ─────────────────────────────────────────────── */
10
12
 
11
- export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallProps) {
13
+ export default function AgentInstall({ agents, t, onRefresh, mode = 'mcp', activeSkillName = 'mindos' }: McpAgentInstallProps) {
12
14
  const m = t.settings?.mcp;
13
15
  const [selected, setSelected] = useState<Set<string>>(new Set());
14
16
  const [transport, setTransport] = useState<'auto' | 'stdio' | 'http'>('auto');
@@ -31,7 +33,8 @@ export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallPr
31
33
  });
32
34
  };
33
35
 
34
- const handleInstall = async () => {
36
+ /* ── MCP mode: install MCP config via API ── */
37
+ const handleMcpInstall = async () => {
35
38
  if (selected.size === 0) return;
36
39
  setInstalling(true);
37
40
  setMessage(null);
@@ -50,7 +53,6 @@ export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallPr
50
53
  }),
51
54
  transport,
52
55
  ...(transport === 'http' ? { url: httpUrl, token: httpToken } : {}),
53
- // For auto mode, pass http settings for agents that need it
54
56
  ...(transport === 'auto' ? { url: httpUrl, token: httpToken } : {}),
55
57
  };
56
58
  const res = await apiFetch<{ results: Array<{ agent: string; status: string; message?: string }> }>('/api/mcp/install', {
@@ -81,8 +83,18 @@ export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallPr
81
83
  return agent?.preferredTransport === 'http';
82
84
  }));
83
85
 
86
+ /* ═══ CLI mode: show skill install commands ═══ */
87
+ if (mode === 'cli') {
88
+ return <CliSkillInstall agents={agents} m={m} activeSkillName={activeSkillName} />;
89
+ }
90
+
91
+ /* ═══ MCP mode: full MCP config install ═══ */
84
92
  return (
85
93
  <div className="space-y-3 pt-2">
94
+ <p className="text-xs text-muted-foreground">
95
+ {m?.mcpInstallDesc ?? 'Install MCP config + Skill to detected agents on this machine.'}
96
+ </p>
97
+
86
98
  {/* Agent list */}
87
99
  <div className="space-y-1">
88
100
  {agents.map(agent => (
@@ -109,7 +121,6 @@ export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallPr
109
121
  {agent.present ? (m?.detected ?? 'Detected') : (m?.notFound ?? 'Not found')}
110
122
  </span>
111
123
  )}
112
- {/* Scope selector */}
113
124
  {selected.has(agent.key) && agent.hasProjectScope && agent.hasGlobalScope && (
114
125
  <CustomSelect
115
126
  value={scopes[agent.key] || 'project'}
@@ -126,12 +137,10 @@ export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallPr
126
137
  ))}
127
138
  </div>
128
139
 
129
- {/* Select detected / Clear buttons */}
140
+ {/* Quick select */}
130
141
  <div className="flex gap-2 text-xs pt-1">
131
142
  <button type="button"
132
- onClick={() => setSelected(new Set(
133
- agents.filter(a => !a.installed && a.present).map(a => a.key)
134
- ))}
143
+ onClick={() => setSelected(new Set(agents.filter(a => !a.installed && a.present).map(a => a.key)))}
135
144
  className="px-2.5 py-1 rounded-md border border-[var(--amber)] text-[var(--amber)] transition-colors hover:bg-muted/50">
136
145
  {m?.selectDetected ?? 'Select Detected'}
137
146
  </button>
@@ -163,32 +172,20 @@ export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallPr
163
172
  <div className="space-y-2 pl-5 text-xs">
164
173
  <div className="space-y-1">
165
174
  <label className="text-muted-foreground">{m?.httpUrl ?? 'MCP URL'}</label>
166
- <input
167
- type="text"
168
- value={httpUrl}
169
- onChange={e => setHttpUrl(e.target.value)}
170
- className="w-full px-2.5 py-1.5 text-xs rounded-md border border-border bg-background font-mono text-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring"
171
- />
175
+ <input type="text" value={httpUrl} onChange={e => setHttpUrl(e.target.value)}
176
+ className="w-full px-2.5 py-1.5 text-xs rounded-md border border-border bg-background font-mono text-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring" />
172
177
  </div>
173
178
  <div className="space-y-1">
174
179
  <label className="text-muted-foreground">{m?.httpToken ?? 'Auth Token'}</label>
175
- <input
176
- type="password"
177
- value={httpToken}
178
- onChange={e => setHttpToken(e.target.value)}
179
- placeholder="Bearer token"
180
- className="w-full px-2.5 py-1.5 text-xs rounded-md border border-border bg-background font-mono text-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring"
181
- />
180
+ <input type="password" value={httpToken} onChange={e => setHttpToken(e.target.value)} placeholder="Bearer token"
181
+ className="w-full px-2.5 py-1.5 text-xs rounded-md border border-border bg-background font-mono text-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring" />
182
182
  </div>
183
183
  </div>
184
184
  )}
185
185
 
186
186
  {/* Install button */}
187
- <button
188
- onClick={handleInstall}
189
- disabled={selected.size === 0 || installing}
190
- className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg transition-colors disabled:opacity-40 disabled:cursor-not-allowed bg-[var(--amber)] text-[var(--amber-foreground)]"
191
- >
187
+ <button onClick={handleMcpInstall} disabled={selected.size === 0 || installing}
188
+ className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg transition-colors disabled:opacity-40 disabled:cursor-not-allowed bg-[var(--amber)] text-[var(--amber-foreground)]">
192
189
  {installing && <Loader2 size={12} className="animate-spin" />}
193
190
  {installing ? (m?.installing ?? 'Installing...') : (m?.installSelected ?? 'Install Selected')}
194
191
  </button>
@@ -206,3 +203,73 @@ export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallPr
206
203
  </div>
207
204
  );
208
205
  }
206
+
207
+ /* ── CLI Skill Install — generates per-agent CLI commands ── */
208
+
209
+ function CliSkillInstall({ agents, m, activeSkillName }: {
210
+ agents: AgentInfo[];
211
+ m: Record<string, any> | undefined;
212
+ activeSkillName: string;
213
+ }) {
214
+ const [selectedAgent, setSelectedAgent] = useState(agents[0]?.key ?? 'claude-code');
215
+ const agent = agents.find(a => a.key === selectedAgent);
216
+ const cmd = `npx skills add GeminiLight/MindOS --skill ${activeSkillName} -a ${selectedAgent} -g -y`;
217
+
218
+ const handleCopy = async () => {
219
+ const ok = await copyToClipboard(cmd);
220
+ if (ok) toast.copy();
221
+ };
222
+
223
+ const connected = agents.filter(a => a.present && a.installed);
224
+ const detected = agents.filter(a => a.present && !a.installed);
225
+ const notFound = agents.filter(a => !a.present);
226
+
227
+ return (
228
+ <div className="space-y-3 pt-2">
229
+ <p className="text-xs text-muted-foreground">
230
+ {m?.cliInstallDesc ?? 'Install the MindOS Skill to your agent so it can operate your knowledge base.'}
231
+ </p>
232
+
233
+ {/* Agent selector */}
234
+ <CustomSelect
235
+ value={selectedAgent}
236
+ onChange={setSelectedAgent}
237
+ size="sm"
238
+ options={[
239
+ ...(connected.length > 0 ? [{ label: m?.connectedGroup ?? 'Connected', options: connected.map(a => ({ value: a.key, label: a.name })) }] : []),
240
+ ...(detected.length > 0 ? [{ label: m?.detectedGroup ?? 'Detected', options: detected.map(a => ({ value: a.key, label: a.name })) }] : []),
241
+ ...(notFound.length > 0 ? [{ label: m?.notFoundGroup ?? 'Not Installed', options: notFound.map(a => ({ value: a.key, label: a.name })) }] : []),
242
+ ]}
243
+ />
244
+
245
+ {/* Status */}
246
+ {agent && (
247
+ <div className="flex items-center gap-2 text-2xs">
248
+ {agent.present && agent.installed ? (
249
+ <span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full font-medium bg-success/10 text-success">
250
+ <CheckCircle2 size={10} /> {m?.tagConnected ?? 'Connected'}
251
+ </span>
252
+ ) : agent.present ? (
253
+ <span className="text-muted-foreground">{m?.detected ?? 'Detected'}</span>
254
+ ) : (
255
+ <span className="text-muted-foreground">{m?.notFound ?? 'Not found'}</span>
256
+ )}
257
+ {agent.installedSkillCount != null && agent.installedSkillCount > 0 && (
258
+ <span className="text-muted-foreground">{agent.installedSkillCount} {m?.skillsInstalled ?? 'skills installed'}</span>
259
+ )}
260
+ </div>
261
+ )}
262
+
263
+ {/* Command */}
264
+ <div className="flex items-center gap-1.5">
265
+ <code className="flex-1 text-[10px] font-mono bg-muted/50 border border-border rounded-lg px-2.5 py-2 text-muted-foreground select-all overflow-x-auto whitespace-nowrap">
266
+ {cmd}
267
+ </code>
268
+ <button onClick={handleCopy}
269
+ className="p-1.5 rounded-md border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors shrink-0">
270
+ <Copy size={11} />
271
+ </button>
272
+ </div>
273
+ </div>
274
+ );
275
+ }
@@ -29,7 +29,7 @@ export default function SkillsSection({ t }: McpSkillsSectionProps) {
29
29
  const [createError, setCreateError] = useState('');
30
30
 
31
31
  const [search, setSearch] = useState('');
32
- const [builtinCollapsed, setBuiltinCollapsed] = useState(false);
32
+ const [builtinCollapsed, setBuiltinCollapsed] = useState(true);
33
33
  const [editing, setEditing] = useState<string | null>(null);
34
34
  const [editContent, setEditContent] = useState('');
35
35
  const [editError, setEditError] = useState('');