@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,15 +1,20 @@
1
1
  /**
2
2
  * mindos file — Knowledge base file operations
3
3
  *
4
- * Subcommands: list, read, create, delete, rename, move, search
4
+ * Subcommands: list, read, write, create, append, edit-section, insert-heading,
5
+ * append-csv, delete, rename, move, search, backlinks, recent, history
5
6
  * Supports --json for agent consumption
6
7
  */
7
8
 
8
- import { existsSync, readFileSync, writeFileSync, unlinkSync, renameSync, mkdirSync, readdirSync, statSync } from 'node:fs';
9
+ import { existsSync, readFileSync, writeFileSync, appendFileSync, unlinkSync, renameSync, mkdirSync, readdirSync, statSync, openSync, readSync, closeSync } from 'node:fs';
9
10
  import { resolve, basename, dirname, relative } from 'node:path';
11
+ import { execFileSync } from 'node:child_process';
10
12
  import { bold, dim, cyan, green, red, yellow } from '../lib/colors.js';
11
13
  import { loadConfig } from '../lib/config.js';
12
14
  import { output, isJsonMode, EXIT } from '../lib/command.js';
15
+ import { isRemoteMode, apiCall } from '../lib/remote.js';
16
+ import { replaceSection, insertAfterHeading, listHeadings } from '../lib/markdown.js';
17
+ import { escapeCsvRow } from '../lib/csv.js';
13
18
 
14
19
  function getMindRoot() {
15
20
  loadConfig();
@@ -33,74 +38,101 @@ function resolvePath(root, filePath) {
33
38
  export const meta = {
34
39
  name: 'file',
35
40
  group: 'Knowledge',
36
- summary: 'File content operations (list, read, create, delete, rename, search)',
41
+ summary: 'Manage files (list, read, write, edit, search, ...)',
37
42
  usage: 'mindos file <subcommand>',
38
43
  flags: {
39
44
  '--space <name>': 'Filter by space name',
40
45
  '--json': 'Output as JSON',
41
- '--recursive, -r': 'Recursive listing',
46
+ '--content <text>': 'Content for write/create/append',
47
+ '-H, --heading <h>': 'Target heading for edit-section/insert-heading',
48
+ '--row <csv>': 'CSV row values (comma-separated)',
49
+ '--limit <n>': 'Limit results (recent, history)',
42
50
  },
43
51
  examples: [
44
52
  'mindos file list',
45
- 'mindos file list --space "Work"',
46
53
  'mindos file read "notes/meeting.md"',
47
- 'mindos file create "notes/idea.md" --content "# My Idea"',
48
- 'mindos file search "RAG implementation"',
49
- 'mindos file delete "notes/old.md"',
54
+ 'mindos file write "notes/plan.md" --content "# Plan"',
55
+ 'mindos file append "log/journal.md" --content "New entry"',
56
+ 'mindos file edit-section "plan.md" -H "## Status" --content "Done"',
57
+ 'mindos file append-csv "tracker.csv" --row "2026-04-04,done,30min"',
58
+ 'mindos file backlinks "concepts/RAG.md"',
59
+ 'mindos file recent --limit 5',
60
+ 'mindos file history "notes/meeting.md"',
50
61
  ],
51
62
  };
52
63
 
53
64
  export async function run(args, flags) {
54
65
  const sub = args[0];
55
- const root = getMindRoot();
66
+ loadConfig();
56
67
 
57
- if (!sub || flags.help || flags.h) {
58
- printFileHelp();
68
+ if (!sub) {
69
+ printHelp();
59
70
  return;
60
71
  }
61
72
 
73
+ // Remote mode: delegate to HTTP API
74
+ if (isRemoteMode()) {
75
+ return remoteFileDispatch(sub, args, flags);
76
+ }
77
+
78
+ const root = getMindRoot();
79
+
62
80
  switch (sub) {
63
- case 'list': return fileList(root, args.slice(1), flags);
64
- case 'ls': return fileList(root, args.slice(1), flags);
65
- case 'read': return fileRead(root, args[1], flags);
66
- case 'cat': return fileRead(root, args[1], flags);
81
+ case 'list': case 'ls': return fileList(root, args.slice(1), flags);
82
+ case 'read': case 'cat': return fileRead(root, args[1], flags);
83
+ case 'write': return fileWrite(root, args[1], flags);
67
84
  case 'create': return fileCreate(root, args[1], flags);
68
- case 'delete': return fileDelete(root, args[1], flags);
69
- case 'rm': return fileDelete(root, args[1], flags);
70
- case 'rename': return fileRename(root, args[1], args[2], flags);
71
- case 'mv': return fileRename(root, args[1], args[2], flags);
72
- case 'move': return fileRename(root, args[1], args[2], flags);
85
+ case 'append': return fileAppend(root, args[1], flags);
86
+ case 'edit-section': return fileEditSection(root, args[1], flags);
87
+ case 'insert-heading': return fileInsertHeading(root, args[1], flags);
88
+ case 'append-csv': return fileAppendCsv(root, args[1], flags);
89
+ case 'delete': case 'rm': return fileDelete(root, args[1], flags);
90
+ case 'rename': case 'mv': case 'move': return fileRename(root, args[1], args[2], flags);
73
91
  case 'search': return fileSearch(root, args.slice(1).join(' '), flags);
92
+ case 'backlinks': return fileBacklinks(root, args[1], flags);
93
+ case 'recent': return fileRecent(root, flags);
94
+ case 'history': return fileHistory(root, args[1], flags);
74
95
  case 'mkdir':
75
96
  console.log(dim('Moved to: mindos space mkdir <path>'));
76
97
  process.exit(EXIT.ARGS);
77
98
  default:
78
99
  console.error(red(`Unknown subcommand: ${sub}`));
79
- console.error(dim('Available: list, read, create, delete, rename, move, mkdir, search'));
100
+ printHelp();
80
101
  process.exit(EXIT.ERROR);
81
102
  }
82
103
  }
83
104
 
84
- function printFileHelp() {
105
+ export function printHelp() {
106
+ const sub = (s, w = 24) => cyan(s.padEnd(w));
85
107
  console.log(`
86
108
  ${bold('mindos file')} — Knowledge base file operations
87
109
 
88
- ${bold('Subcommands:')}
89
- ${cyan('list'.padEnd(20))}${dim('List files in knowledge base')}
90
- ${cyan('read <path>'.padEnd(20))}${dim('Read file content')}
91
- ${cyan('create <path>'.padEnd(20))}${dim('Create a new file (--content "...")')}
92
- ${cyan('delete <path>'.padEnd(20))}${dim('Delete a file')}
93
- ${cyan('rename <old> <new>'.padEnd(20))}${dim('Rename or move a file')}
94
- ${cyan('search <query>'.padEnd(20))}${dim('Search files by content')}
95
-
96
- ${bold('Aliases:')} ls=list, cat=read, rm=delete, mv=rename
97
-
98
- ${bold('Examples:')}
99
- ${dim('mindos file list')}
110
+ ${bold('SUBCOMMANDS')}
111
+ ${sub('list')}${dim('List files in knowledge base')}
112
+ ${sub('read <path>')}${dim('Read file content')}
113
+ ${sub('write <path>')}${dim('Write/overwrite file (--content or stdin)')}
114
+ ${sub('create <path>')}${dim('Create a new file (--content "...")')}
115
+ ${sub('append <path>')}${dim('Append content to file end')}
116
+ ${sub('edit-section <path>')}${dim('Replace a markdown section (-H "## Heading")')}
117
+ ${sub('insert-heading <path>')}${dim('Insert after a heading (-H "## Heading")')}
118
+ ${sub('append-csv <path>')}${dim('Append row to CSV (--row "a,b,c")')}
119
+ ${sub('delete <path>')}${dim('Delete a file')}
120
+ ${sub('rename <old> <new>')}${dim('Rename or move a file')}
121
+ ${sub('search <query>')}${dim('Search files by content')}
122
+ ${sub('backlinks <path>')}${dim('Find files that reference this file')}
123
+ ${sub('recent')}${dim('Show recently modified files')}
124
+ ${sub('history <path>')}${dim('Show git commit history')}
125
+
126
+ ${bold('ALIASES')} ls=list cat=read rm=delete mv=rename
127
+
128
+ ${bold('EXAMPLES')}
100
129
  ${dim('mindos file list --json')}
101
130
  ${dim('mindos file read "notes/meeting.md"')}
102
- ${dim('mindos file create "ideas/new.md" --content "# New Idea"')}
103
- ${dim('mindos file search "machine learning"')}
131
+ ${dim('mindos file write "plan.md" --content "# Plan"')}
132
+ ${dim('mindos file edit-section "plan.md" -H "## Status" --content "Done"')}
133
+ ${dim('mindos file append-csv "habits.csv" --row "2026-04-04,run,30min"')}
134
+ ${dim('mindos file backlinks "concepts/RAG.md"')}
135
+ ${dim('mindos file recent --limit 5')}
104
136
  `);
105
137
  }
106
138
 
@@ -291,6 +323,309 @@ function fileSearch(root, query, flags) {
291
323
  console.log();
292
324
  }
293
325
 
326
+ // ── New subcommands (CLI-first agent mode) ────────────────────────────────
327
+
328
+ function fileWrite(root, filePath, flags) {
329
+ if (!filePath) {
330
+ console.error(red('Usage: mindos file write <path> --content "..."'));
331
+ process.exit(EXIT.ERROR);
332
+ }
333
+ const full = resolvePath(root, filePath);
334
+ const content = flags.content ?? '';
335
+ if (!content && process.stdin.isTTY) {
336
+ console.error(red('No content provided. Use --content "..." or pipe via stdin.'));
337
+ process.exit(EXIT.ERROR);
338
+ }
339
+
340
+ mkdirSync(dirname(full), { recursive: true });
341
+ writeFileSync(full, content, 'utf-8');
342
+
343
+ if (isJsonMode(flags)) {
344
+ output({ ok: true, path: filePath, size: content.length }, flags);
345
+ return;
346
+ }
347
+ console.log(`${green('✔')} Wrote ${content.length} characters to ${cyan(filePath)}`);
348
+ }
349
+
350
+ function fileAppend(root, filePath, flags) {
351
+ if (!filePath) {
352
+ console.error(red('Usage: mindos file append <path> --content "..."'));
353
+ process.exit(EXIT.ERROR);
354
+ }
355
+ const full = resolvePath(root, filePath);
356
+ if (!existsSync(full)) {
357
+ console.error(red(`File not found: ${filePath}`));
358
+ process.exit(EXIT.ERROR);
359
+ }
360
+ const content = flags.content ?? '';
361
+ if (!content) {
362
+ console.error(red('No content provided. Use --content "..."'));
363
+ process.exit(EXIT.ERROR);
364
+ }
365
+
366
+ const stat = statSync(full);
367
+ let separator = '';
368
+ if (stat.size > 0) {
369
+ const readLen = Math.min(8, stat.size);
370
+ const fd = openSync(full, 'r');
371
+ try {
372
+ const buf = Buffer.alloc(readLen);
373
+ readSync(fd, buf, 0, readLen, Math.max(0, stat.size - readLen));
374
+ const tail = buf.toString('utf-8');
375
+ separator = tail.endsWith('\n') ? '' : '\n';
376
+ } finally {
377
+ closeSync(fd);
378
+ }
379
+ }
380
+ appendFileSync(full, separator + content, 'utf-8');
381
+
382
+ if (isJsonMode(flags)) {
383
+ output({ ok: true, path: filePath, appended: content.length }, flags);
384
+ return;
385
+ }
386
+ console.log(`${green('✔')} Appended ${content.length} characters to ${cyan(filePath)}`);
387
+ }
388
+
389
+ function fileEditSection(root, filePath, flags) {
390
+ const heading = flags.heading || flags.H;
391
+ if (!filePath || !heading) {
392
+ console.error(red('Usage: mindos file edit-section <path> -H "## Heading" --content "..."'));
393
+ process.exit(EXIT.ERROR);
394
+ }
395
+ const full = resolvePath(root, filePath);
396
+ if (!existsSync(full)) {
397
+ console.error(red(`File not found: ${filePath}`));
398
+ process.exit(EXIT.ERROR);
399
+ }
400
+ const content = flags.content ?? '';
401
+ const original = readFileSync(full, 'utf-8');
402
+ const result = replaceSection(original, heading, content);
403
+
404
+ if (result === null) {
405
+ const headings = listHeadings(original);
406
+ console.error(red(`Heading "${heading}" not found in ${filePath}`));
407
+ if (headings.length > 0) {
408
+ console.error(dim('Available headings:'));
409
+ for (const h of headings) console.error(dim(` ${h}`));
410
+ }
411
+ process.exit(EXIT.ERROR);
412
+ }
413
+
414
+ writeFileSync(full, result, 'utf-8');
415
+
416
+ if (isJsonMode(flags)) {
417
+ output({ ok: true, path: filePath, heading }, flags);
418
+ return;
419
+ }
420
+ console.log(`${green('✔')} Updated section "${heading}" in ${cyan(filePath)}`);
421
+ }
422
+
423
+ function fileInsertHeading(root, filePath, flags) {
424
+ const heading = flags.heading || flags.H;
425
+ if (!filePath || !heading) {
426
+ console.error(red('Usage: mindos file insert-heading <path> -H "## Heading" --content "..."'));
427
+ process.exit(EXIT.ERROR);
428
+ }
429
+ const full = resolvePath(root, filePath);
430
+ if (!existsSync(full)) {
431
+ console.error(red(`File not found: ${filePath}`));
432
+ process.exit(EXIT.ERROR);
433
+ }
434
+ const content = flags.content ?? '';
435
+ const original = readFileSync(full, 'utf-8');
436
+ const result = insertAfterHeading(original, heading, content);
437
+
438
+ if (result === null) {
439
+ const headings = listHeadings(original);
440
+ console.error(red(`Heading "${heading}" not found in ${filePath}`));
441
+ if (headings.length > 0) {
442
+ console.error(dim('Available headings:'));
443
+ for (const h of headings) console.error(dim(` ${h}`));
444
+ }
445
+ process.exit(EXIT.ERROR);
446
+ }
447
+
448
+ writeFileSync(full, result, 'utf-8');
449
+
450
+ if (isJsonMode(flags)) {
451
+ output({ ok: true, path: filePath, heading }, flags);
452
+ return;
453
+ }
454
+ console.log(`${green('✔')} Inserted content after "${heading}" in ${cyan(filePath)}`);
455
+ }
456
+
457
+ function fileAppendCsv(root, filePath, flags) {
458
+ if (!filePath) {
459
+ console.error(red('Usage: mindos file append-csv <path> --row "val1,val2,val3"'));
460
+ process.exit(EXIT.ERROR);
461
+ }
462
+ if (!filePath.endsWith('.csv')) {
463
+ console.error(red('Only .csv files support row append'));
464
+ process.exit(EXIT.ERROR);
465
+ }
466
+ const rowStr = flags.row;
467
+ if (!rowStr) {
468
+ console.error(red('No row provided. Use --row "val1,val2,val3"'));
469
+ process.exit(EXIT.ERROR);
470
+ }
471
+ const full = resolvePath(root, filePath);
472
+ let values;
473
+ if (typeof rowStr === 'string' && rowStr.startsWith('[')) {
474
+ try { values = JSON.parse(rowStr); } catch { values = rowStr.split(',').map(v => v.trim()); }
475
+ } else {
476
+ values = typeof rowStr === 'string' ? rowStr.split(',').map(v => v.trim()) : [String(rowStr)];
477
+ }
478
+ const line = escapeCsvRow(values) + '\n';
479
+
480
+ mkdirSync(dirname(full), { recursive: true });
481
+ appendFileSync(full, line, 'utf-8');
482
+
483
+ const content = readFileSync(full, 'utf-8');
484
+ const newRowCount = content.trim().split('\n').length;
485
+
486
+ if (isJsonMode(flags)) {
487
+ output({ ok: true, path: filePath, newRowCount }, flags);
488
+ return;
489
+ }
490
+ console.log(`${green('✔')} Appended row to ${cyan(filePath)} (${newRowCount} rows total)`);
491
+ }
492
+
493
+ function fileBacklinks(root, filePath, flags) {
494
+ if (!filePath) {
495
+ console.error(red('Usage: mindos file backlinks <path>'));
496
+ process.exit(EXIT.ERROR);
497
+ }
498
+ resolvePath(root, filePath);
499
+
500
+ const bname = basename(filePath, '.md');
501
+ const escapedTarget = filePath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
502
+ const escapedBname = bname.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
503
+ const patterns = [
504
+ new RegExp(`\\[\\[${escapedBname}(?:[|#][^\\]]*)?\\]\\]`, 'i'),
505
+ new RegExp(`\\[\\[${escapedTarget}(?:[|#][^\\]]*)?\\]\\]`, 'i'),
506
+ new RegExp(`\\[[^\\]]+\\]\\(${escapedTarget}(?:#[^)]*)?\\)`, 'i'),
507
+ ];
508
+
509
+ const results = [];
510
+ const allFiles = walkFiles(root, root);
511
+ for (const f of allFiles) {
512
+ if (f.path === filePath) continue;
513
+ if (!f.name.endsWith('.md')) continue;
514
+ try {
515
+ const content = readFileSync(resolve(root, f.path), 'utf-8');
516
+ if (patterns.some(p => p.test(content))) {
517
+ const lines = content.split('\n');
518
+ let matchLine = 0;
519
+ for (let i = 0; i < lines.length; i++) {
520
+ if (patterns.some(p => p.test(lines[i]))) { matchLine = i + 1; break; }
521
+ }
522
+ results.push({ source: f.path, line: matchLine });
523
+ }
524
+ } catch { /* skip unreadable */ }
525
+ }
526
+
527
+ if (isJsonMode(flags)) {
528
+ output({ path: filePath, count: results.length, backlinks: results }, flags);
529
+ return;
530
+ }
531
+
532
+ if (results.length === 0) {
533
+ console.log(dim(`No files reference "${filePath}"`));
534
+ return;
535
+ }
536
+ console.log(`\n${bold(`Backlinks to "${filePath}" (${results.length}):`)}\n`);
537
+ for (const r of results) {
538
+ console.log(` ${cyan(r.source)} ${dim(`L${r.line}`)}`);
539
+ }
540
+ console.log();
541
+ }
542
+
543
+ function fileRecent(root, flags) {
544
+ const limit = parseInt(flags.limit, 10) || 10;
545
+ const allFiles = walkFiles(root, root);
546
+ const withMtime = allFiles.map(f => {
547
+ const stat = statSync(resolve(root, f.path));
548
+ return { ...f, mtime: stat.mtimeMs, mtimeIso: stat.mtime.toISOString() };
549
+ });
550
+ withMtime.sort((a, b) => b.mtime - a.mtime);
551
+ const recent = withMtime.slice(0, limit);
552
+
553
+ if (isJsonMode(flags)) {
554
+ output({ count: recent.length, files: recent.map(f => ({ path: f.path, mtime: f.mtimeIso })) }, flags);
555
+ return;
556
+ }
557
+
558
+ if (recent.length === 0) {
559
+ console.log(dim('No files found.'));
560
+ return;
561
+ }
562
+ console.log(`\n${bold(`Recently modified (${recent.length}):`)}\n`);
563
+ for (const f of recent) {
564
+ const ago = formatTimeAgo(f.mtime);
565
+ console.log(` ${f.path} ${dim(ago)}`);
566
+ }
567
+ console.log();
568
+ }
569
+
570
+ function fileHistory(root, filePath, flags) {
571
+ if (!filePath) {
572
+ console.error(red('Usage: mindos file history <path>'));
573
+ process.exit(EXIT.ERROR);
574
+ }
575
+ resolvePath(root, filePath);
576
+ const limit = parseInt(flags.limit, 10) || 10;
577
+ const full = resolve(root, filePath);
578
+
579
+ try {
580
+ execFileSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: root, stdio: 'pipe' });
581
+ } catch {
582
+ console.error(red('Knowledge base is not a git repository.'));
583
+ process.exit(EXIT.ERROR);
584
+ }
585
+
586
+ let gitOutput = '';
587
+ try {
588
+ gitOutput = execFileSync(
589
+ 'git',
590
+ ['log', '--follow', '--format=%H%x00%aI%x00%s%x00%an', '-n', String(limit), '--', full],
591
+ { cwd: root, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }
592
+ ).trim();
593
+ } catch { /* no commits or git error */ }
594
+
595
+ const entries = gitOutput
596
+ ? gitOutput.split('\n').map(line => {
597
+ const [hash, date, message, author] = line.split('\0');
598
+ return { hash, date, message, author };
599
+ })
600
+ : [];
601
+
602
+ if (isJsonMode(flags)) {
603
+ output({ path: filePath, count: entries.length, entries }, flags);
604
+ return;
605
+ }
606
+
607
+ if (entries.length === 0) {
608
+ console.log(dim(`No git history for "${filePath}"`));
609
+ return;
610
+ }
611
+ console.log(`\n${bold(`Git history: ${filePath} (${entries.length}):`)}\n`);
612
+ for (const e of entries) {
613
+ console.log(` ${dim(e.hash.slice(0, 8))} ${e.date.slice(0, 10)} ${e.message} ${dim(e.author)}`);
614
+ }
615
+ console.log();
616
+ }
617
+
618
+ function formatTimeAgo(mtimeMs) {
619
+ const diff = Date.now() - mtimeMs;
620
+ const mins = Math.floor(diff / 60000);
621
+ if (mins < 1) return 'just now';
622
+ if (mins < 60) return `${mins}m ago`;
623
+ const hours = Math.floor(mins / 60);
624
+ if (hours < 24) return `${hours}h ago`;
625
+ const days = Math.floor(hours / 24);
626
+ return `${days}d ago`;
627
+ }
628
+
294
629
  function fileMkdir(root, dirPath, flags) {
295
630
  if (!dirPath) {
296
631
  console.error(red('Usage: mindos file mkdir <path>'));
@@ -314,3 +649,183 @@ function fileMkdir(root, dirPath, flags) {
314
649
  }
315
650
  console.log(`${green('✔')} Created directory: ${cyan(dirPath)}`);
316
651
  }
652
+
653
+ // ── Remote mode: all operations via HTTP API ──────────────────────────────
654
+
655
+ async function remoteFileDispatch(sub, args, flags) {
656
+ try {
657
+ switch (sub) {
658
+ case 'list':
659
+ case 'ls': {
660
+ const res = await apiCall('/api/files');
661
+ const data = await res.json();
662
+ if (isJsonMode(flags)) {
663
+ output(data, flags);
664
+ return;
665
+ }
666
+ // data is a tree; flatten for display
667
+ const files = [];
668
+ function walk(nodes, prefix = '') {
669
+ for (const n of nodes || []) {
670
+ const p = prefix ? `${prefix}/${n.name}` : n.name;
671
+ if (n.type === 'file') files.push(p);
672
+ if (n.children) walk(n.children, p);
673
+ }
674
+ }
675
+ walk(data.tree || data);
676
+ console.log(`\n${bold(`Files (${files.length}):`)}\n`);
677
+ for (const f of files) console.log(` ${f}`);
678
+ console.log();
679
+ return;
680
+ }
681
+
682
+ case 'read':
683
+ case 'cat': {
684
+ const filePath = args[1];
685
+ if (!filePath) { console.error(red('Usage: mindos file read <path>')); process.exit(EXIT.ERROR); }
686
+ const res = await apiCall(`/api/file?path=${encodeURIComponent(filePath)}&op=read_file`);
687
+ const data = await res.json();
688
+ if (isJsonMode(flags)) { output(data, flags); return; }
689
+ console.log(data.content ?? '');
690
+ return;
691
+ }
692
+
693
+ case 'create': {
694
+ const filePath = args[1];
695
+ if (!filePath) { console.error(red('Usage: mindos file create <path> --content "..."')); process.exit(EXIT.ERROR); }
696
+ const content = flags.content || `# ${basename(filePath, '.md')}\n`;
697
+ // --force → save_file (overwrite); default → create_file (fail if exists)
698
+ const action = flags.force ? 'save_file' : 'create_file';
699
+ const res = await apiCall('/api/file', {
700
+ method: 'POST',
701
+ body: JSON.stringify({ action, path: filePath, content }),
702
+ });
703
+ const data = await res.json();
704
+ if (isJsonMode(flags)) { output(data, flags); return; }
705
+ console.log(`${green('✔')} ${flags.force ? 'Saved' : 'Created'}: ${cyan(filePath)}`);
706
+ return;
707
+ }
708
+
709
+ case 'delete':
710
+ case 'rm': {
711
+ const filePath = args[1];
712
+ if (!filePath) { console.error(red('Usage: mindos file delete <path>')); process.exit(EXIT.ERROR); }
713
+ const res = await apiCall('/api/file', {
714
+ method: 'POST',
715
+ body: JSON.stringify({ action: 'delete_file', path: filePath }),
716
+ });
717
+ await res.json();
718
+ if (isJsonMode(flags)) { output({ ok: true, deleted: filePath }, flags); return; }
719
+ console.log(`${green('✔')} Deleted: ${filePath}`);
720
+ return;
721
+ }
722
+
723
+ case 'rename':
724
+ case 'mv':
725
+ case 'move': {
726
+ const oldPath = args[1], newPath = args[2];
727
+ if (!oldPath || !newPath) { console.error(red('Usage: mindos file rename <old> <new>')); process.exit(EXIT.ERROR); }
728
+ const action = sub === 'move' ? 'move_file' : 'rename_file';
729
+ const body = action === 'move_file'
730
+ ? { action, path: oldPath, destination: newPath }
731
+ : { action, path: oldPath, newName: newPath };
732
+ const res = await apiCall('/api/file', { method: 'POST', body: JSON.stringify(body) });
733
+ await res.json();
734
+ if (isJsonMode(flags)) { output({ ok: true, from: oldPath, to: newPath }, flags); return; }
735
+ console.log(`${green('✔')} ${sub === 'move' ? 'Moved' : 'Renamed'}: ${oldPath} → ${cyan(newPath)}`);
736
+ return;
737
+ }
738
+
739
+ case 'search': {
740
+ const query = args.slice(1).join(' ');
741
+ if (!query) { console.error(red('Usage: mindos file search <query>')); process.exit(EXIT.ERROR); }
742
+ const limit = flags.limit || 20;
743
+ const res = await apiCall(`/api/search?q=${encodeURIComponent(query)}&limit=${limit}`);
744
+ const data = await res.json();
745
+ if (isJsonMode(flags)) { output(data, flags); return; }
746
+ const results = data.results || [];
747
+ if (results.length === 0) { console.log(dim(`No results for "${query}"`)); return; }
748
+ console.log(`\n${bold(`Search: "${query}" (${results.length} files)`)}\n`);
749
+ for (const r of results) {
750
+ console.log(` ${cyan(r.path || r.file)}`);
751
+ for (const m of (r.matches || []).slice(0, 3)) {
752
+ console.log(` ${dim(`L${m.line}:`)} ${m.text || m.snippet || ''}`);
753
+ }
754
+ }
755
+ console.log();
756
+ return;
757
+ }
758
+
759
+ case 'write': case 'append': case 'edit-section': case 'insert-heading':
760
+ case 'append-csv': case 'backlinks': case 'recent': case 'history': {
761
+ const opMap = {
762
+ 'write': 'save_file', 'append': 'append_to_file',
763
+ 'edit-section': 'update_section', 'insert-heading': 'insert_after_heading',
764
+ 'append-csv': 'append_csv',
765
+ };
766
+ const op = opMap[sub];
767
+ if (op) {
768
+ const filePath = args[1];
769
+ if (!filePath) { console.error(red(`Usage: mindos file ${sub} <path> ...`)); process.exit(EXIT.ERROR); }
770
+ const body = { action: op, path: filePath };
771
+ if (flags.content !== undefined) body.content = flags.content;
772
+ if (flags.heading || flags.H) body.heading = flags.heading || flags.H;
773
+ if (flags.row) body.row = typeof flags.row === 'string' ? flags.row.split(',').map(v => v.trim()) : [String(flags.row)];
774
+ const res = await apiCall('/api/file', { method: 'POST', body: JSON.stringify(body) });
775
+ const data = await res.json();
776
+ if (isJsonMode(flags)) { output(data, flags); return; }
777
+ console.log(`${green('✔')} ${sub} completed: ${cyan(filePath)}`);
778
+ return;
779
+ }
780
+ // backlinks/recent/history — read-only, can delegate to specific API
781
+ if (sub === 'backlinks') {
782
+ const filePath = args[1];
783
+ if (!filePath) { console.error(red('Usage: mindos file backlinks <path>')); process.exit(EXIT.ERROR); }
784
+ const res = await apiCall(`/api/file?op=get_backlinks&path=${encodeURIComponent(filePath)}`);
785
+ const data = await res.json();
786
+ if (isJsonMode(flags)) { output(data, flags); return; }
787
+ const links = data.backlinks || [];
788
+ if (links.length === 0) { console.log(dim(`No files reference "${filePath}"`)); return; }
789
+ console.log(`\n${bold(`Backlinks to "${filePath}" (${links.length}):`)}\n`);
790
+ for (const r of links) console.log(` ${cyan(r.source)} ${dim(`L${r.line}`)}`);
791
+ console.log();
792
+ return;
793
+ }
794
+ if (sub === 'recent') {
795
+ const limit = parseInt(flags.limit, 10) || 10;
796
+ const res = await apiCall(`/api/recent-files?limit=${limit}`);
797
+ const data = await res.json();
798
+ if (isJsonMode(flags)) { output(data, flags); return; }
799
+ const files = data.files || data || [];
800
+ if (files.length === 0) { console.log(dim('No files found.')); return; }
801
+ console.log(`\n${bold(`Recently modified (${files.length}):`)}\n`);
802
+ for (const f of files) console.log(` ${f.path} ${dim(f.modifiedAt || '')}`);
803
+ console.log();
804
+ return;
805
+ }
806
+ if (sub === 'history') {
807
+ const filePath = args[1];
808
+ if (!filePath) { console.error(red('Usage: mindos file history <path>')); process.exit(EXIT.ERROR); }
809
+ const limit = parseInt(flags.limit, 10) || 10;
810
+ const res = await apiCall(`/api/git?action=log&path=${encodeURIComponent(filePath)}&limit=${limit}`);
811
+ const data = await res.json();
812
+ if (isJsonMode(flags)) { output(data, flags); return; }
813
+ const entries = data.entries || data || [];
814
+ if (entries.length === 0) { console.log(dim(`No git history for "${filePath}"`)); return; }
815
+ console.log(`\n${bold(`Git history: ${filePath} (${entries.length}):`)}\n`);
816
+ for (const e of entries) console.log(` ${dim(e.hash?.slice(0, 8) || '')} ${(e.date || '').slice(0, 10)} ${e.message || ''} ${dim(e.author || '')}`);
817
+ console.log();
818
+ return;
819
+ }
820
+ break;
821
+ }
822
+
823
+ default:
824
+ console.error(red(`Unknown subcommand: ${sub}`));
825
+ process.exit(EXIT.ERROR);
826
+ }
827
+ } catch (e) {
828
+ console.error(red(`Remote error: ${e.message}`));
829
+ process.exit(EXIT.ERROR);
830
+ }
831
+ }