@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
@@ -1,37 +1,132 @@
1
+ /**
2
+ * mindos agent — AI Agent: interactive REPL (default) or one-shot (-p)
3
+ *
4
+ * Inspired by Claude Code: bare `mindos agent` enters interactive mode;
5
+ * `mindos agent -p "task"` prints the result and exits.
6
+ *
7
+ * Management subcommands (list/info/stats) are available as sub-routes.
8
+ */
9
+
1
10
  import { bold, dim, cyan, green, red, yellow } from '../lib/colors.js';
2
11
  import { MCP_AGENTS, detectAgentPresence } from '../lib/mcp-agents.js';
3
12
  import { existsSync, readFileSync } from 'node:fs';
4
- import { resolve } from 'node:path';
5
- import { homedir } from 'node:os';
13
+ import { loadConfig } from '../lib/config.js';
6
14
  import { output, isJsonMode, EXIT } from '../lib/command.js';
15
+ import { startRepl } from '../lib/repl.js';
16
+ import { executeOneShot } from '../lib/one-shot.js';
17
+ import { expandHome } from '../lib/path-expand.js';
7
18
 
8
- function expandHome(p) {
9
- return p.startsWith('~/') ? resolve(homedir(), p.slice(2)) : p;
10
- }
19
+ const MANAGEMENT_SUBCOMMANDS = new Set(['list', 'ls', 'info', 'stats', 'help']);
11
20
 
12
21
  export const meta = {
13
22
  name: 'agent',
14
- group: 'Knowledge',
15
- summary: 'AI Agent management (list/info)',
16
- usage: 'mindos agent <subcommand>',
23
+ group: 'AI',
24
+ summary: 'AI Agent: interactive REPL or one-shot (-p)',
25
+ usage: 'mindos agent [-p "<task>"]',
26
+ flags: {
27
+ '-p, --print': 'Non-interactive: run task and print result',
28
+ '--file <path>': 'Attach a file as context',
29
+ '--max-steps <n>': 'Max agent steps (default: 20)',
30
+ '--json': 'Output as JSON (implies -p)',
31
+ '--port <port>': 'MindOS web port (default: 3456)',
32
+ },
33
+ examples: [
34
+ 'mindos agent # interactive REPL',
35
+ 'mindos agent -p "Organize my inbox"',
36
+ 'mindos agent "Summarize notes" # also one-shot',
37
+ 'mindos agent list # list detected agents',
38
+ ],
17
39
  };
18
40
 
19
41
  export async function run(args, flags) {
20
42
  const sub = args[0];
21
- if (!sub || flags.help || flags.h) {
22
- console.log(bold('mindos agent') + ' AI Agent management\n');
23
- console.log('Subcommands:');
24
- console.log(' list List detected AI agents');
25
- console.log(' info <agent-key> Show agent details\n');
26
- console.log('Keys: ' + Object.keys(MCP_AGENTS).join(', '));
43
+
44
+ // Management subcommands always take priority
45
+ if (sub && MANAGEMENT_SUBCOMMANDS.has(sub)) {
46
+ if (sub === 'help') { printHelp(); return; }
47
+ if (sub === 'list' || sub === 'ls') return agentList(flags);
48
+ if (sub === 'info') return agentInfo(args[1], flags);
49
+ if (sub === 'stats') return agentStats(flags);
27
50
  return;
28
51
  }
29
- if (sub === 'list' || sub === 'ls') return agentList(flags);
30
- if (sub === 'info') return agentInfo(args[1], flags);
31
- console.error(red('Unknown subcommand: ' + sub));
32
- process.exit(EXIT.ARGS);
52
+
53
+ // Determine mode: -p / --print / --json / bare task → print; otherwise interactive
54
+ const isPrintMode = flags.p || flags.print || isJsonMode(flags) || (sub != null);
55
+
56
+ if (isPrintMode) {
57
+ const task = args.join(' ');
58
+ if (!task) {
59
+ console.error(red('No task provided.'));
60
+ console.error(dim('Usage: mindos agent -p "<task>"'));
61
+ console.error(dim(' mindos agent (interactive mode)'));
62
+ process.exit(EXIT.ARGS);
63
+ }
64
+ return agentExecute(task, flags);
65
+ }
66
+
67
+ // Interactive REPL (default)
68
+ return agentInteractive(flags);
69
+ }
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Interactive REPL
73
+ // ---------------------------------------------------------------------------
74
+
75
+ async function agentInteractive(flags) {
76
+ loadConfig();
77
+ const port = flags.port || process.env.MINDOS_WEB_PORT || '3456';
78
+ const token = process.env.MINDOS_AUTH_TOKEN || '';
79
+ const baseUrl = `http://localhost:${port}`;
80
+
81
+ const maxSteps = (() => {
82
+ if (!flags['max-steps']) return undefined;
83
+ const n = parseInt(flags['max-steps'], 10);
84
+ return (!Number.isNaN(n) && n > 0) ? n : undefined;
85
+ })();
86
+
87
+ await startRepl({
88
+ baseUrl,
89
+ token,
90
+ mode: 'agent',
91
+ prompt: 'agent> ',
92
+ welcome: bold('MindOS Agent') + dim(' (interactive) — full tool access'),
93
+ showTools: true,
94
+ attachedFiles: flags.file ? [flags.file] : undefined,
95
+ maxSteps,
96
+ });
33
97
  }
34
98
 
99
+ // ---------------------------------------------------------------------------
100
+ // Print Mode — One-shot Task Execution
101
+ // ---------------------------------------------------------------------------
102
+
103
+ async function agentExecute(task, flags) {
104
+ loadConfig();
105
+ const port = flags.port || process.env.MINDOS_WEB_PORT || '3456';
106
+ const token = process.env.MINDOS_AUTH_TOKEN || '';
107
+
108
+ const maxSteps = (() => {
109
+ if (!flags['max-steps']) return undefined;
110
+ const n = parseInt(flags['max-steps'], 10);
111
+ return (!Number.isNaN(n) && n > 0) ? n : undefined;
112
+ })();
113
+
114
+ await executeOneShot({
115
+ baseUrl: `http://localhost:${port}`,
116
+ token,
117
+ message: task,
118
+ mode: 'agent',
119
+ showTools: true,
120
+ maxSteps,
121
+ attachedFiles: flags.file ? [flags.file] : undefined,
122
+ json: isJsonMode(flags),
123
+ });
124
+ }
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Agent Management — List / Info / Stats
128
+ // ---------------------------------------------------------------------------
129
+
35
130
  function hasMindosConfig(agent) {
36
131
  const paths = [agent.global, agent.project].filter(Boolean).map(expandHome);
37
132
  for (const p of paths) {
@@ -76,12 +171,13 @@ function agentList(flags) {
76
171
  function agentInfo(key, flags) {
77
172
  if (!key) {
78
173
  console.error(red('Usage: mindos agent info <agent-key>'));
79
- process.exit(EXIT.ERROR);
174
+ process.exit(EXIT.ARGS);
80
175
  }
81
176
  const agent = MCP_AGENTS[key];
82
177
  if (!agent) {
83
178
  console.error(red('Unknown agent: ' + key));
84
- process.exit(EXIT.ERROR);
179
+ console.error(dim('Available: ' + Object.keys(MCP_AGENTS).join(', ')));
180
+ process.exit(EXIT.NOT_FOUND);
85
181
  }
86
182
 
87
183
  const installed = detectAgentPresence(key);
@@ -108,3 +204,43 @@ function agentInfo(key, flags) {
108
204
  if (!connected && installed) console.log('\n Connect: mindos mcp install ' + key);
109
205
  console.log('');
110
206
  }
207
+
208
+ function agentStats(flags) {
209
+ if (isJsonMode(flags)) {
210
+ output({ message: 'Agent usage statistics are not yet available.' }, flags);
211
+ return;
212
+ }
213
+ console.log(dim('\n Agent usage statistics are not yet available.'));
214
+ console.log(dim(' This feature will be added in a future release.\n'));
215
+ }
216
+
217
+ // ---------------------------------------------------------------------------
218
+ // Help
219
+ // ---------------------------------------------------------------------------
220
+
221
+ export function printHelp() {
222
+ console.log(`
223
+ ${bold('mindos agent')} — AI Agent with full tool access
224
+
225
+ ${bold('Interactive (default):')}
226
+ ${cyan('mindos agent')} Enter multi-turn REPL
227
+ ${dim('Commands inside REPL: /clear, /exit')}
228
+
229
+ ${bold('Non-interactive (-p):')}
230
+ ${cyan('mindos agent -p "<task>"')} Run task, print result, exit
231
+ ${cyan('mindos agent "<task>"')} Same (shorthand)
232
+
233
+ ${bold('Manage agents:')}
234
+ ${cyan('mindos agent list')} List detected AI agents
235
+ ${cyan('mindos agent info <agent-key>')} Show agent details
236
+ ${cyan('mindos agent stats')} Usage statistics
237
+
238
+ ${bold('Options:')}
239
+ ${dim('-p, --print')} Non-interactive mode
240
+ ${dim('--file <path>')} Attach file as context
241
+ ${dim('--max-steps <n>')} Max agent steps (default: 20)
242
+ ${dim('--json')} JSON output (implies -p)
243
+
244
+ ${bold('Note:')} For lightweight Q&A (read-only), use ${cyan('mindos ask')}.
245
+ `);
246
+ }
@@ -1,23 +1,26 @@
1
1
  import { bold, dim, cyan, red } from '../lib/colors.js';
2
2
  import { loadConfig } from '../lib/config.js';
3
- import { EXIT } from '../lib/command.js';
3
+ import { EXIT, printCommandHelp } from '../lib/command.js';
4
4
 
5
5
  export const meta = {
6
6
  name: 'api',
7
- group: 'Knowledge',
8
- summary: 'Raw API passthrough (GET/POST/PUT/DELETE any endpoint)',
7
+ group: 'Config',
8
+ summary: 'Raw API passthrough (GET/POST/PUT/DELETE)',
9
9
  usage: 'mindos api <METHOD> <path>',
10
+ flags: {
11
+ '--body <json>': 'Request body as JSON string',
12
+ '--port <port>': 'MindOS web port',
13
+ },
14
+ examples: [
15
+ 'mindos api GET /api/health',
16
+ 'mindos api GET /api/files',
17
+ 'mindos api POST /api/ask --body \'{"messages":[...],"mode":"chat"}\'',
18
+ ],
10
19
  };
11
20
 
12
21
  export async function run(args, flags) {
13
- if (args.length < 2 || flags.help || flags.h) {
14
- console.log(bold('mindos api') + ' — Raw API passthrough\n');
15
- console.log('Usage: mindos api <METHOD> <path> [--body <json>]');
16
- console.log('Methods: GET, POST, PUT, PATCH, DELETE\n');
17
- console.log('Examples:');
18
- console.log(' mindos api GET /api/health');
19
- console.log(' mindos api GET /api/files');
20
- console.log(' mindos api POST /api/ask --body \'{"question":"..."}\'');
22
+ if (args.length < 2) {
23
+ printCommandHelp({ meta });
21
24
  return;
22
25
  }
23
26
 
@@ -1,101 +1,112 @@
1
1
  /**
2
- * mindos ask — AI question answering via local MindOS API
2
+ * mindos ask — AI Chat: interactive REPL (default) or one-shot (-p)
3
+ *
4
+ * Uses 'chat' mode: read-only tools, lower step cap, smaller prompt.
5
+ * For full agent mode with tool execution, use `mindos agent`.
3
6
  */
4
7
 
5
- import { bold, dim, cyan, green, red } from '../lib/colors.js';
8
+ import { bold, dim, cyan, red } from '../lib/colors.js';
6
9
  import { loadConfig } from '../lib/config.js';
7
- import { output, isJsonMode, EXIT } from '../lib/command.js';
10
+ import { isJsonMode, EXIT } from '../lib/command.js';
11
+ import { startRepl } from '../lib/repl.js';
12
+ import { executeOneShot } from '../lib/one-shot.js';
8
13
 
9
14
  export const meta = {
10
15
  name: 'ask',
11
- group: 'Knowledge',
12
- summary: 'Ask AI a question using your knowledge base',
13
- usage: 'mindos ask "<question>"',
16
+ group: 'AI',
17
+ summary: 'Chat with your knowledge base (read-only)',
18
+ usage: 'mindos ask [-p "<question>"]',
14
19
  flags: {
15
- '--json': 'Output as JSON',
20
+ '-p, --print': 'Non-interactive: answer and exit',
21
+ '--file <path>': 'Attach a file as context',
22
+ '--json': 'Output as JSON (implies -p)',
16
23
  '--port <port>': 'MindOS web port (default: 3456)',
17
24
  },
18
25
  examples: [
19
- 'mindos ask "Summarize my meeting notes from today"',
20
- 'mindos ask "What are the key points in my RAG research?"',
21
- 'mindos ask "List all TODOs across my notes" --json',
26
+ 'mindos ask # interactive chat',
27
+ 'mindos ask -p "What is RAG?"',
28
+ 'mindos ask "Summarize my notes" # also one-shot',
22
29
  ],
23
30
  };
24
31
 
25
32
  export async function run(args, flags) {
26
- const question = args.join(' ');
33
+ const isPrintMode = flags.p || flags.print || isJsonMode(flags) || (args.length > 0);
27
34
 
28
- if (!question || flags.help || flags.h) {
29
- console.log(`
30
- ${bold('mindos ask')} — Ask AI using your knowledge base
31
-
32
- ${bold('Usage:')}
33
- ${cyan('mindos ask "<question>"')}
35
+ if (isPrintMode) {
36
+ const question = args.join(' ');
37
+ if (!question) {
38
+ console.error(red('No question provided. Usage: mindos ask -p "<question>"'));
39
+ process.exit(EXIT.ARGS);
40
+ }
41
+ return askExecute(question, flags);
42
+ }
34
43
 
35
- ${bold('Examples:')}
36
- ${dim('mindos ask "Summarize my meeting notes"')}
37
- ${dim('mindos ask "What are the key insights from my research?" --json')}
44
+ // Interactive REPL (default)
45
+ return askInteractive(flags);
46
+ }
38
47
 
39
- ${bold('Note:')} MindOS must be running (mindos start).
40
- `);
41
- return;
42
- }
48
+ // ---------------------------------------------------------------------------
49
+ // Interactive REPL
50
+ // ---------------------------------------------------------------------------
43
51
 
52
+ async function askInteractive(flags) {
44
53
  loadConfig();
45
54
  const port = flags.port || process.env.MINDOS_WEB_PORT || '3456';
46
55
  const token = process.env.MINDOS_AUTH_TOKEN || '';
47
56
  const baseUrl = `http://localhost:${port}`;
48
57
 
49
- // Check if MindOS is running
50
- try {
51
- const healthRes = await fetch(`${baseUrl}/api/health`);
52
- if (!healthRes.ok) throw new Error();
53
- } catch {
54
- console.error(red('MindOS is not running. Start it with: mindos start'));
55
- process.exit(EXIT.ERROR);
56
- }
58
+ await startRepl({
59
+ baseUrl,
60
+ token,
61
+ mode: 'chat',
62
+ prompt: 'chat> ',
63
+ welcome: bold('MindOS Chat') + dim(' (interactive) read-only knowledge access'),
64
+ showTools: false,
65
+ attachedFiles: flags.file ? [flags.file] : undefined,
66
+ });
67
+ }
57
68
 
58
- if (!isJsonMode(flags)) {
59
- process.stdout.write(dim('Thinking...'));
60
- }
69
+ // ---------------------------------------------------------------------------
70
+ // Print Mode — One-shot Q&A
71
+ // ---------------------------------------------------------------------------
72
+
73
+ async function askExecute(question, flags) {
74
+ loadConfig();
75
+ const port = flags.port || process.env.MINDOS_WEB_PORT || '3456';
76
+ const token = process.env.MINDOS_AUTH_TOKEN || '';
61
77
 
62
- try {
63
- const headers = { 'Content-Type': 'application/json' };
64
- if (token) headers['Authorization'] = `Bearer ${token}`;
78
+ await executeOneShot({
79
+ baseUrl: `http://localhost:${port}`,
80
+ token,
81
+ message: question,
82
+ mode: 'chat',
83
+ attachedFiles: flags.file ? [flags.file] : undefined,
84
+ json: isJsonMode(flags),
85
+ });
86
+ }
65
87
 
66
- const res = await fetch(`${baseUrl}/api/ask`, {
67
- method: 'POST',
68
- headers,
69
- body: JSON.stringify({ question }),
70
- });
88
+ // ---------------------------------------------------------------------------
89
+ // Help
90
+ // ---------------------------------------------------------------------------
71
91
 
72
- if (!res.ok) {
73
- const errText = await res.text();
74
- throw new Error(`API error (${res.status}): ${errText}`);
75
- }
92
+ export function printHelp() {
93
+ console.log(`
94
+ ${bold('mindos ask')} Chat with your knowledge base (read-only)
76
95
 
77
- const data = await res.json();
96
+ ${bold('Interactive (default):')}
97
+ ${cyan('mindos ask')} Enter multi-turn chat
98
+ ${dim('Commands inside REPL: /clear, /exit')}
78
99
 
79
- if (isJsonMode(flags)) {
80
- output(data, flags);
81
- return;
82
- }
100
+ ${bold('Non-interactive (-p):')}
101
+ ${cyan('mindos ask -p "<question>"')} Answer and exit
102
+ ${cyan('mindos ask "<question>"')} Same (shorthand)
83
103
 
84
- // Clear "Thinking..." line
85
- process.stdout.write('\r' + ' '.repeat(40) + '\r');
104
+ ${bold('Options:')}
105
+ ${dim('-p, --print')} Non-interactive mode
106
+ ${dim('--file <path>')} Attach file as context
107
+ ${dim('--json')} JSON output (implies -p)
86
108
 
87
- if (data.answer) {
88
- console.log(data.answer);
89
- } else if (data.text) {
90
- console.log(data.text);
91
- } else {
92
- console.log(JSON.stringify(data, null, 2));
93
- }
94
- } catch (err) {
95
- if (!isJsonMode(flags)) {
96
- process.stdout.write('\r' + ' '.repeat(40) + '\r');
97
- }
98
- console.error(red(err.message));
99
- process.exit(EXIT.ERROR);
100
- }
109
+ ${bold('Note:')} Chat mode uses read-only tools for knowledge access.
110
+ For full agent mode with write tools, use ${cyan('mindos agent')}.
111
+ `);
101
112
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * mindos build — Build for production
3
+ */
4
+
5
+ import { resolve } from 'node:path';
6
+ import { ROOT } from '../lib/constants.js';
7
+ import { ensureAppDeps, cleanNextDir, writeBuildStamp } from '../lib/build.js';
8
+ import { execInherited } from '../lib/shell.js';
9
+
10
+ const NEXT_BIN = resolve(ROOT, 'app', 'node_modules', '.bin', 'next');
11
+
12
+ export const meta = {
13
+ name: 'build',
14
+ group: 'Service',
15
+ summary: 'Build for production',
16
+ usage: 'mindos build',
17
+ };
18
+
19
+ export const run = (args) => {
20
+ const extra = args.join(' ');
21
+ ensureAppDeps({ force: true });
22
+ cleanNextDir();
23
+ execInherited('node scripts/gen-renderer-index.js', ROOT);
24
+ execInherited(`${NEXT_BIN} build --webpack ${extra}`, resolve(ROOT, 'app'));
25
+ writeBuildStamp();
26
+ };
@@ -0,0 +1,170 @@
1
+ /**
2
+ * mindos config — View and update MindOS configuration
3
+ */
4
+
5
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
6
+ import { resolve } from 'node:path';
7
+ import { CONFIG_PATH, ROOT } from '../lib/constants.js';
8
+ import { bold, dim, cyan, green, red } from '../lib/colors.js';
9
+ import { EXIT } from '../lib/command.js';
10
+
11
+ export const meta = {
12
+ name: 'config',
13
+ group: 'Config',
14
+ summary: 'View or update configuration',
15
+ usage: 'mindos config <subcommand>',
16
+ flags: {
17
+ '--json': 'Output as JSON',
18
+ },
19
+ examples: [
20
+ 'mindos config show',
21
+ 'mindos config set startMode dev',
22
+ 'mindos config unset sync.remote',
23
+ 'mindos config validate',
24
+ ],
25
+ };
26
+
27
+ const maskKey = (val) => {
28
+ if (!val) return val;
29
+ if (val.length <= 8) return '****';
30
+ return val.slice(0, 6) + '****';
31
+ };
32
+
33
+ const readConfig = () => {
34
+ if (!existsSync(CONFIG_PATH)) {
35
+ console.error(red('No config found. Run `mindos onboard` first.'));
36
+ process.exit(EXIT.ERROR);
37
+ }
38
+ try {
39
+ return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
40
+ } catch {
41
+ console.error(red('Failed to parse config file.'));
42
+ process.exit(EXIT.ERROR);
43
+ }
44
+ };
45
+
46
+ const coerceValue = (v) => {
47
+ if (v === 'true') return true;
48
+ if (v === 'false') return false;
49
+ if (v === 'null') return null;
50
+ if (v === '""' || v === "''") return '';
51
+ if (v.trim() !== '' && !isNaN(Number(v))) return Number(v);
52
+ return v;
53
+ };
54
+
55
+ export const run = (args, flags) => {
56
+ const sub = args[0];
57
+
58
+ if (sub === 'show') {
59
+ const config = readConfig();
60
+ const display = JSON.parse(JSON.stringify(config));
61
+ if (display.ai?.providers?.anthropic?.apiKey)
62
+ display.ai.providers.anthropic.apiKey = maskKey(display.ai.providers.anthropic.apiKey);
63
+ if (display.ai?.providers?.openai?.apiKey)
64
+ display.ai.providers.openai.apiKey = maskKey(display.ai.providers.openai.apiKey);
65
+ if (display.ai?.anthropicApiKey)
66
+ display.ai.anthropicApiKey = maskKey(display.ai.anthropicApiKey);
67
+ if (display.ai?.openaiApiKey)
68
+ display.ai.openaiApiKey = maskKey(display.ai.openaiApiKey);
69
+ if (display.authToken)
70
+ display.authToken = maskKey(display.authToken);
71
+ if (display.webPassword)
72
+ display.webPassword = maskKey(display.webPassword);
73
+
74
+ if (flags.json) {
75
+ console.log(JSON.stringify(display, null, 2));
76
+ return;
77
+ }
78
+ const pkgVersion = (() => { try { return JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf-8')).version; } catch { return '?'; } })();
79
+ console.log(`\n${bold('MindOS Config')} ${dim(`v${pkgVersion}`)} ${dim(CONFIG_PATH)}\n`);
80
+ console.log(JSON.stringify(display, null, 2));
81
+ console.log();
82
+ return;
83
+ }
84
+
85
+ if (sub === 'validate') {
86
+ const config = readConfig();
87
+ const issues = [];
88
+ if (!config.mindRoot) issues.push('missing required field: mindRoot');
89
+ if (!config.ai?.provider) issues.push('missing field: ai.provider');
90
+ if (config.ai?.provider === 'anthropic') {
91
+ const key = config.ai?.providers?.anthropic?.apiKey || config.ai?.anthropicApiKey;
92
+ if (!key) issues.push('ai.provider is "anthropic" but no API key found');
93
+ }
94
+ if (config.ai?.provider === 'openai') {
95
+ const key = config.ai?.providers?.openai?.apiKey || config.ai?.openaiApiKey;
96
+ if (!key) issues.push('ai.provider is "openai" but no API key found');
97
+ }
98
+ if (issues.length) {
99
+ console.error(`\n${red('✘ Config has issues:')}`);
100
+ issues.forEach(i => console.error(` ${red('•')} ${i}`));
101
+ console.error(`\n ${dim('Run `mindos onboard` to fix.\n')}`);
102
+ process.exit(EXIT.ERROR);
103
+ }
104
+ console.log(`\n${green('✔ Config is valid')}\n`);
105
+ return;
106
+ }
107
+
108
+ if (sub === 'set') {
109
+ const key = args[1];
110
+ const val = args[2];
111
+ if (!key || val === undefined) {
112
+ console.error(red('Usage: mindos config set <key> <value>'));
113
+ console.error(dim(' Examples:'));
114
+ console.error(dim(' mindos config set port 3002'));
115
+ console.error(dim(' mindos config set mcpPort 8788'));
116
+ console.error(dim(' mindos config set ai.provider openai'));
117
+ process.exit(EXIT.ARGS);
118
+ }
119
+ const config = readConfig();
120
+ const parts = key.split('.');
121
+ let obj = config;
122
+ for (let i = 0; i < parts.length - 1; i++) {
123
+ if (typeof obj[parts[i]] !== 'object' || !obj[parts[i]]) obj[parts[i]] = {};
124
+ obj = obj[parts[i]];
125
+ }
126
+ const coerced = coerceValue(val);
127
+ obj[parts[parts.length - 1]] = coerced;
128
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
129
+ console.log(`${green('✔')} Set ${cyan(key)} = ${bold(String(coerced))}`);
130
+ return;
131
+ }
132
+
133
+ if (sub === 'unset') {
134
+ const key = args[1];
135
+ if (!key) {
136
+ console.error(red('Usage: mindos config unset <key>'));
137
+ process.exit(EXIT.ARGS);
138
+ }
139
+ const config = readConfig();
140
+ const parts = key.split('.');
141
+ let obj = config;
142
+ for (let i = 0; i < parts.length - 1; i++) {
143
+ if (!obj[parts[i]]) { console.log(dim(`Key "${key}" not found`)); return; }
144
+ obj = obj[parts[i]];
145
+ }
146
+ if (!(parts[parts.length - 1] in obj)) { console.log(dim(`Key "${key}" not found`)); return; }
147
+ delete obj[parts[parts.length - 1]];
148
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
149
+ console.log(`${green('✔')} Removed ${cyan(key)}`);
150
+ return;
151
+ }
152
+
153
+ // No subcommand → show help
154
+ const row = (c, d) => ` ${cyan(c.padEnd(32))}${dim(d)}`;
155
+ console.log(`
156
+ ${bold('mindos config')} — view and update MindOS configuration
157
+
158
+ ${bold('Subcommands:')}
159
+ ${row('mindos config show', 'Print current config (API keys masked)')}
160
+ ${row('mindos config validate', 'Validate config file')}
161
+ ${row('mindos config set <key> <v>', 'Update a single field (dot-notation supported)')}
162
+ ${row('mindos config unset <key>', 'Remove a config field')}
163
+
164
+ ${bold('Examples:')}
165
+ ${dim('mindos config set port 3002')}
166
+ ${dim('mindos config set ai.provider openai')}
167
+ ${dim('mindos config set setupPending false')}
168
+ ${dim('mindos config unset webPassword')}
169
+ `);
170
+ };